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.
AddEffectSegment.c
////////// |
// |
// File: AddEffectSegment.c |
// |
// Contains: Sample code for adding a visual effect to a segment of a movie. |
// |
// Written by: Tim Monroe |
// |
// Copyright: © 1999 by Apple Computer, Inc., all rights reserved. |
// |
// Change History (most recent first): |
// |
// <2> 05/06/99 rtm tested on Mac and Windows; works fine |
// <1> 05/05/99 rtm first file; some functions based on some existing code |
// in QTShowEffect |
// |
// This file contains some sample code that adds a QuickTime effect to a part of a movie; |
// we assume that the movie already exists and that it contains one or more video tracks. |
// Our job here is to add a specified effect at a specified time. For one- or two-source |
// effects, the principal value in this code consists in illustrating how to duplicate the |
// portion(s) of the existing video track(s) for use as the effects source track(s). |
// |
////////// |
#include "AddEffectSegment.h" |
////////// |
// |
// QTEffSeg_AddEffectToMovieSegment |
// Add the specified effect to occur at the specified time and duration. |
// |
////////// |
OSErr QTEffSeg_AddEffectToMovieSegment (Movie theMovie, OSType theEffectType, long theNumSources, TimeValue theStartTime, TimeValue theDuration) |
{ |
Track myVidTrack1 = NULL; |
Track myVidTrack2 = NULL; |
Track mySrcTrack1 = NULL; |
Track mySrcTrack2 = NULL; |
Media mySrcMedia1 = NULL; |
Media mySrcMedia2 = NULL; |
Track myEffectTrack = NULL; |
Media myEffectMedia = NULL; |
Fixed myWidth, myHeight; |
TimeScale myTimeScale; |
TimeValue mySampleTime; |
Rect myRect; |
QTAtomContainer myInputMap = NULL; |
QTAtomContainer myEffectDesc = NULL; |
ImageDescriptionHandle mySampleDesc = NULL; |
OSType myEffectName1 = kSourceNoneName; |
OSType myEffectName2 = kSourceNoneName; |
short myLayer; |
OSErr myErr = noErr; |
////////// |
// |
// get some information about the movie |
// |
////////// |
// make sure we were passed a valid movie |
if (theMovie == NULL) |
return(paramErr); |
myTimeScale = GetMovieTimeScale(theMovie); |
GetMovieBox(theMovie, &myRect); |
myLayer = QTEffSeg_GetFrontmostTrackLayer(theMovie, VideoMediaType); |
myWidth = (myRect.right - myRect.left) << 16; |
myHeight = (myRect.bottom - myRect.top) << 16; |
////////// |
// |
// retrieve the original video track(s), create the effect's source track(s) and media, |
// then set the new source track(s) to reference the data in the original video track(s) |
// |
////////// |
switch (theNumSources) { |
case 2: |
myVidTrack2 = GetMovieIndTrackType(theMovie, 2, VideoMediaType, movieTrackMediaType | movieTrackEnabledOnly); |
if (myVidTrack2 == NULL) |
return(paramErr); |
mySrcTrack2 = NewMovieTrack(theMovie, myWidth, myHeight, kNoVolume); |
if (mySrcTrack2 == NULL) |
return(paramErr); |
mySrcMedia2 = NewTrackMedia(mySrcTrack2, VideoMediaType, myTimeScale, NULL, 0); |
if (mySrcMedia2 == NULL) |
return(paramErr); |
#if COPY_MOVIE_MEDIA |
myErr = BeginMediaEdits(mySrcMedia2); |
if (myErr != noErr) |
return(myErr); |
#endif |
myErr = CopyTrackSettings(myVidTrack2, mySrcTrack2); |
myErr = InsertTrackSegment(myVidTrack2, mySrcTrack2, theStartTime, theDuration, theStartTime); |
if (myErr != noErr) |
return(myErr); |
#if COPY_MOVIE_MEDIA |
EndMediaEdits(mySrcMedia2); |
#endif |
myEffectName2 = kSourceTwoName; |
// note that we fall through here! |
case 1: |
myVidTrack1 = GetMovieIndTrackType(theMovie, 1, VideoMediaType, movieTrackMediaType | movieTrackEnabledOnly); |
if (myVidTrack1 == NULL) |
return(paramErr); |
mySrcTrack1 = NewMovieTrack(theMovie, myWidth, myHeight, kNoVolume); |
if (mySrcTrack1 == NULL) |
return(paramErr); |
mySrcMedia1 = NewTrackMedia(mySrcTrack1, VideoMediaType, myTimeScale, NULL, 0); |
if (mySrcMedia1 == NULL) |
return(paramErr); |
#if COPY_MOVIE_MEDIA |
myErr = BeginMediaEdits(mySrcMedia1); |
if (myErr != noErr) |
return(myErr); |
#endif |
myErr = CopyTrackSettings(myVidTrack1, mySrcTrack1); |
myErr = InsertTrackSegment(myVidTrack1, mySrcTrack1, theStartTime, theDuration, theStartTime); |
if (myErr != noErr) |
return(myErr); |
#if COPY_MOVIE_MEDIA |
EndMediaEdits(mySrcMedia1); |
#endif |
myEffectName1 = kSourceOneName; |
break; |
case 0: |
// for 0-source effects, we don't need to create any new source track |
break; |
default: |
return(paramErr); |
} |
////////// |
// |
// create the effects track and media |
// |
////////// |
myEffectTrack = NewMovieTrack(theMovie, myWidth, myHeight, kNoVolume); |
if (myEffectTrack == NULL) |
return(GetMoviesError()); |
myEffectMedia = NewTrackMedia(myEffectTrack, VideoMediaType, myTimeScale, NULL, 0); |
if (myEffectMedia == NULL) |
return(GetMoviesError()); |
// create an effect sample description |
mySampleDesc = QTEffSeg_MakeSampleDescription(theEffectType, myWidth >> 16, myHeight >> 16); |
if (mySampleDesc == NULL) |
goto bail; |
// create an effect description |
myEffectDesc = QTEffSeg_CreateEffectDescription(theEffectType, myEffectName1, myEffectName2); |
// add the effect description as a sample to the effect track media |
BeginMediaEdits(myEffectMedia); |
myErr = AddMediaSample(myEffectMedia, (Handle)myEffectDesc, 0, GetHandleSize((Handle)myEffectDesc), theDuration, (SampleDescriptionHandle)mySampleDesc, 1, 0, &mySampleTime); |
if (myErr != noErr) |
goto bail; |
EndMediaEdits(myEffectMedia); |
// add the media sample to the effects track |
myErr = InsertMediaIntoTrack(myEffectTrack, theStartTime, mySampleTime, theDuration, fixed1); |
if (myErr != noErr) |
goto bail; |
////////// |
// |
// create the input map and add references for the source track(s) |
// |
////////// |
myErr = QTNewAtomContainer(&myInputMap); |
if (myErr != noErr) |
goto bail; |
if (mySrcTrack1 != NULL) { |
myErr = QTEffSeg_AddTrackReferenceToInputMap(myInputMap, myEffectTrack, mySrcTrack1, kSourceOneName); |
if (myErr != noErr) |
goto bail; |
} |
if (mySrcTrack2 != NULL) { |
myErr = QTEffSeg_AddTrackReferenceToInputMap(myInputMap, myEffectTrack, mySrcTrack2, kSourceTwoName); |
if (myErr != noErr) |
goto bail; |
} |
// add the input map to the effects track |
myErr = SetMediaInputMap(myEffectMedia, myInputMap); |
if (myErr != noErr) |
goto bail; |
////////// |
// |
// do any required positioning and graphics mode manipulation |
// |
////////// |
SetTrackLayer(myEffectTrack, myLayer - 1); // in front of any existing video track |
switch (theNumSources) { |
case 2: |
break; |
case 1: |
break; |
case 0: { |
RGBColor myColor; |
myColor.red = 0; // (good for fire, not so good for clouds) |
myColor.green = 0; |
myColor.blue = 0; |
MediaSetGraphicsMode(GetMediaHandler(myEffectMedia), transparent, &myColor); |
break; |
} |
} |
bail: |
if (mySampleDesc != NULL) |
DisposeHandle((Handle)mySampleDesc); |
if (myInputMap != NULL) |
QTDisposeAtomContainer(myInputMap); |
return(myErr); |
} |
////////// |
// |
// QTEffSeg_CreateEffectDescription |
// Create an effect description for zero, one, or two sources. |
// |
// The effect description specifies which video effect is desired and the parameters for that effect. |
// It also describes the source(s) for the effect. An effect description is simply an atom container |
// that holds atoms with the appropriate information. |
// |
// Note that because we are creating an atom container, we must pass big-endian data (hence the calls |
// to EndianU32_NtoB). |
// |
// The caller is responsible for disposing of the returned atom container, by calling QTDisposeAtomContainer. |
// |
////////// |
QTAtomContainer QTEffSeg_CreateEffectDescription (OSType theEffectName, OSType theSourceName1, OSType theSourceName2) |
{ |
QTAtomContainer myEffectDesc = NULL; |
OSType myType; |
OSErr myErr = noErr; |
// create a new, empty effect description |
myErr = QTNewAtomContainer(&myEffectDesc); |
if (myErr != noErr) |
goto bail; |
// create the effect ID atom: the atom type is kParameterWhatName, and the atom ID is kParameterWhatID |
myType = EndianU32_NtoB(theEffectName); |
myErr = QTInsertChild(myEffectDesc, kParentAtomIsContainer, kParameterWhatName, kParameterWhatID, 0, sizeof(myType), &myType, NULL); |
if (myErr != noErr) |
goto bail; |
// add the first source, if it's not kSourceNoneName |
if (theSourceName1 != kSourceNoneName) { |
myType = EndianU32_NtoB(theSourceName1); |
myErr = QTInsertChild(myEffectDesc, kParentAtomIsContainer, kEffectSourceName, 1, 0, sizeof(myType), &myType, NULL); |
if (myErr != noErr) |
goto bail; |
} |
// add the second source, if it's not kSourceNoneName |
if (theSourceName2 != kSourceNoneName) { |
myType = EndianU32_NtoB(theSourceName2); |
myErr = QTInsertChild(myEffectDesc, kParentAtomIsContainer, kEffectSourceName, 2, 0, sizeof(myType), &myType, NULL); |
} |
bail: |
return(myEffectDesc); |
} |
////////// |
// |
// QTEffSeg_AddTrackReferenceToInputMap |
// Add a track reference to the specified input map. |
// |
////////// |
OSErr QTEffSeg_AddTrackReferenceToInputMap (QTAtomContainer theInputMap, Track theTrack, Track theSrcTrack, OSType theSrcName) |
{ |
OSErr myErr = noErr; |
QTAtom myInputAtom; |
long myRefIndex; |
OSType myType; |
myErr = AddTrackReference(theTrack, theSrcTrack, kTrackModifierReference, &myRefIndex); |
if (myErr != noErr) |
goto bail; |
// add a reference atom to the input map |
myErr = QTInsertChild(theInputMap, kParentAtomIsContainer, kTrackModifierInput, myRefIndex, 0, 0, NULL, &myInputAtom); |
if (myErr != noErr) |
goto bail; |
// add two child atoms to the parent reference atom |
myType = EndianU32_NtoB(kTrackModifierTypeImage); |
myErr = QTInsertChild(theInputMap, myInputAtom, kTrackModifierType, 1, 0, sizeof(myType), &myType, NULL); |
if (myErr != noErr) |
goto bail; |
myType = EndianU32_NtoB(theSrcName); |
myErr = QTInsertChild(theInputMap, myInputAtom, kEffectDataSourceType, 1, 0, sizeof(myType), &myType, NULL); |
bail: |
return(myErr); |
} |
////////// |
// |
// QTEffSeg_MakeSampleDescription |
// Return a new image description with default and specified values. |
// |
////////// |
ImageDescriptionHandle QTEffSeg_MakeSampleDescription (OSType theEffectType, short theWidth, short theHeight) |
{ |
ImageDescriptionHandle mySampleDesc = NULL; |
#if USES_MAKE_IMAGE_DESC_FOR_EFFECT |
OSErr myErr = noErr; |
// create a new sample description |
myErr = MakeImageDescriptionForEffect(theEffectType, &mySampleDesc); |
if (myErr != noErr) |
return(NULL); |
#else |
// create a new sample description |
mySampleDesc = (ImageDescriptionHandle)NewHandleClear(sizeof(ImageDescription)); |
if (mySampleDesc == NULL) |
return(NULL); |
// fill in the fields of the sample description |
(**mySampleDesc).cType = theEffectType; |
(**mySampleDesc).idSize = sizeof(ImageDescription); |
(**mySampleDesc).hRes = 72L << 16; |
(**mySampleDesc).vRes = 72L << 16; |
(**mySampleDesc).frameCount = 1; |
(**mySampleDesc).depth = 0; |
(**mySampleDesc).clutID = -1; |
#endif |
(**mySampleDesc).vendor = kAppleManufacturer; |
(**mySampleDesc).temporalQuality = codecNormalQuality; |
(**mySampleDesc).spatialQuality = codecNormalQuality; |
(**mySampleDesc).width = theWidth; |
(**mySampleDesc).height = theHeight; |
return(mySampleDesc); |
} |
////////// |
// |
// QTEffSeg_GetFrontmostTrackLayer |
// Return the layer number of the frontmost track of the specified kind in a movie. |
// |
////////// |
short QTEffSeg_GetFrontmostTrackLayer (Movie theMovie, OSType theTrackType) |
{ |
short myLayer = 0; |
short myIndex = 1; |
Track myTrack = NULL; |
// get the layer number of the first track of the specified kind; |
// if no track of that kind exists in the movie, return 0 |
myTrack = GetMovieIndTrackType(theMovie, 1, theTrackType, movieTrackMediaType | movieTrackEnabledOnly); |
if (myTrack == NULL) |
return(myLayer); |
myLayer = GetTrackLayer(myTrack); |
// see if any of the remaining tracks have lower layer numbers |
while (myTrack != NULL) { |
if (myLayer > GetTrackLayer(myTrack)) |
myLayer = GetTrackLayer(myTrack); |
myIndex++; |
myTrack = GetMovieIndTrackType(theMovie, myIndex, theTrackType, movieTrackMediaType | movieTrackEnabledOnly); |
} |
return(myLayer); |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-02-25