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.
QTCapture.c
////////// |
// |
// File: QTCapture.c |
// |
// Contains: Capturing QuickTime movies with the Sequence Grabber. |
// Based on HackTV sample code. |
// |
// Written by: Tim Monroe |
// |
// Copyright: © 2001 by Apple Computer, Inc., all rights reserved. |
// |
// Change History (most recent first): |
// |
// <3> 09/13/01 rtm added video bottleneck code |
// <2> 04/06/01 rtm added monitor window refresh using SGGrabPict |
// <1> 04/04/01 rtm first file, using HackTV sources; Carbonized code |
// |
////////// |
////////// |
// |
// header files |
// |
////////// |
#include "QTCapture.h" |
////////// |
// |
// global variables |
// |
////////// |
// Sequence Grabber info |
SeqGrabComponent gSeqGrabber = NULL; |
SGChannel gVideoChannel = 0; |
SGChannel gSoundChannel = 0; |
// monitor window info |
DialogPtr gMonitor = NULL; |
PicHandle gMonitorPICT = NULL; |
Rect gActiveVideoRect; |
// monitor window sizes |
Boolean gFullSize = false; |
Boolean gHalfSize = true; |
Boolean gQuarterSize = false; |
// state variables |
Boolean gRecordVideo = true; |
Boolean gRecordSound = true; |
Boolean gSplitTracks = false; |
#if TARGET_OS_MAC |
extern gAppInForeground; |
extern gHasNewDialogCalls; |
#endif |
////////// |
// |
// QTCap_Init |
// Initialize movie capture. |
// |
////////// |
ComponentResult QTCap_Init (void) |
{ |
ComponentResult myErr = noErr; |
// open the sequence grabber component |
gSeqGrabber = OpenDefaultComponent(SeqGrabComponentType, 0); |
if (gSeqGrabber == NULL) { |
myErr = cantOpenHandler; |
goto bail; |
} |
// open the monitor window |
gMonitor = GetNewDialog(kMonitorDLOGID, NULL, (WindowPtr)-1L); |
if (gMonitor == NULL) { |
myErr = memFullErr; |
goto bail; |
} |
SetPortDialogPort(gMonitor); |
MacMoveWindow(GetDialogWindow(gMonitor), 10, 30 + GetMBarHeight(), 0); |
// initialize the sequence grabber |
myErr = SGInitialize(gSeqGrabber); |
if (myErr == noErr) { |
// configure the sequence grabber component |
myErr = SGSetGWorld(gSeqGrabber, GetDialogPort(gMonitor), NULL); |
if (myErr != noErr) |
goto bail; |
// create a video channel |
myErr = SGNewChannel(gSeqGrabber, VideoMediaType, &gVideoChannel); |
if ((gVideoChannel != NULL) && (myErr == noErr)) { |
short myWidth; |
short myHeight; |
Rect myRect; |
myErr = SGGetSrcVideoBounds(gVideoChannel, &gActiveVideoRect); |
if (myErr == noErr) { |
myWidth = (gActiveVideoRect.right - gActiveVideoRect.left) / 2; |
myHeight = (gActiveVideoRect.bottom - gActiveVideoRect.top) / 2; |
SizeWindow(GetDialogWindow(gMonitor), myWidth, myHeight, false); |
} |
myErr = SGSetChannelUsage(gVideoChannel, seqGrabPreview | seqGrabRecord | seqGrabPlayDuringRecord); |
if (myErr == noErr) { |
GetPortBounds(GetDialogPort(gMonitor), &myRect); |
myErr = SGSetChannelBounds(gVideoChannel, &myRect); |
} |
// if an error occurred while configuring video channel, dispose of it |
if (myErr != noErr) { |
SGDisposeChannel(gSeqGrabber, gVideoChannel); |
gVideoChannel = NULL; |
} |
} |
// create a sound channel |
myErr = SGNewChannel(gSeqGrabber, SoundMediaType, &gSoundChannel); |
if ((gSoundChannel != NULL) && (myErr == noErr)) { |
Handle myRates = NULL; |
myErr = SGSetChannelUsage(gSoundChannel, seqGrabPreview | seqGrabRecord); |
if (myErr == noErr) { |
// set the volume low to prevent feedback when we start the preview |
// (in case the mic is anywhere near the speaker) |
myErr = SGSetChannelVolume(gSoundChannel, 0x0010); |
} |
// add some sample rates to the Sound settings dialog box Rate pop-up menu |
myRates = NewHandleClear(5 * sizeof(Fixed)); |
if (myRates != NULL) { |
*((long *)(*myRates) + 0) = Long2Fix(8000); // add 8kHz rate |
*((long *)(*myRates) + 1) = Long2Fix(11025); // add 11kHz rate |
*((long *)(*myRates) + 2) = Long2Fix(16000); // add 16kHz rate |
*((long *)(*myRates) + 3) = Long2Fix(22050); // add 22kHz rate |
*((long *)(*myRates) + 4) = Long2Fix(32000); // add 32kHz rate |
SGSetAdditionalSoundRates(gSoundChannel, myRates); |
DisposeHandle(myRates); |
} |
// if an error occurred while configuring sound channel, dispose of it |
if (myErr != noErr) { |
SGDisposeChannel(gSeqGrabber, gSoundChannel); |
gSoundChannel = NULL; |
} |
} |
} |
// display the monitor window |
MacShowWindow(GetDialogWindow(gMonitor)); |
// start previewing |
if (myErr == noErr) |
myErr = SGStartPreview(gSeqGrabber); |
bail: |
// if an error occurred, clean up |
if (myErr != noErr) |
QTCap_Stop(); |
return(myErr); |
} |
////////// |
// |
// QTCap_Stop |
// Shut down movie capture. |
// |
////////// |
void QTCap_Stop (void) |
{ |
if (gSeqGrabber != NULL) { |
SGStop(gSeqGrabber); |
CloseComponent(gSeqGrabber); |
gSeqGrabber = NULL; |
} |
if (gMonitor != NULL) { |
DisposeDialog(gMonitor); |
gMonitor = NULL; |
} |
} |
////////// |
// |
// QTCap_SetTrackFile |
// Prompt the user for the file in which to save a track's data. |
// |
////////// |
static ComponentResult QTCap_SetTrackFile (SGChannel theChannel, char *thePrompt, char *theDefaultName) |
{ |
FSSpec myFile; |
Boolean myIsSelected = false; |
Boolean myIsReplacing = false; |
StringPtr myPrompt = QTUtils_ConvertCToPascalString(thePrompt); |
StringPtr myFileName = QTUtils_ConvertCToPascalString(theDefaultName); |
SGOutput myOutput; |
AliasHandle myAliasHandle = NULL; |
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; |
myErr = QTNewAlias(&myFile, &myAliasHandle, true); |
if (myErr != noErr) |
goto bail; |
// create an output from this file |
myErr = SGNewOutput(gSeqGrabber, (Handle)myAliasHandle, rAliasType, seqGrabToDisk, &myOutput); |
if (myErr != noErr) |
goto bail; |
// associate this output with the specified channel |
myErr = SGSetChannelOutput(gSeqGrabber, theChannel, myOutput); |
bail: |
free(myPrompt); |
free(myFileName); |
if (myAliasHandle != NULL) |
DisposeHandle((Handle)myAliasHandle); |
return(myErr); |
} |
////////// |
// |
// QTCap_Record |
// Record the video and audio data into one or more files. |
// |
////////// |
void QTCap_Record (void) |
{ |
FSSpec myFile; |
Boolean myIsSelected = false; |
Boolean myIsReplacing = false; |
StringPtr myPrompt = QTUtils_ConvertCToPascalString(kCapSavePrompt); |
StringPtr myFileName = QTUtils_ConvertCToPascalString(kCapSaveMovieFileName); |
long myFlags = createMovieFileDontOpenFile | createMovieFileDontCreateMovie | createMovieFileDontCreateResFile; |
ComponentResult myErr = noErr; |
// stop everything while the dialogs are up |
SGStop(gSeqGrabber); |
// prompt the user for new file name |
QTFrame_PutFile(myPrompt, myFileName, &myFile, &myIsSelected, &myIsReplacing); |
myErr = myIsSelected ? noErr : userCanceledErr; |
if (myErr != noErr) |
goto bail; |
// delete any existing the movie file, if the user so instructs |
if (myIsReplacing) |
DeleteMovieFile(&myFile); |
myErr = SGSetDataOutput(gSeqGrabber, &myFile, seqGrabToDisk); |
if (myErr != noErr) |
goto bail; |
// ask for separate video and sound track files, if requested |
if ((gSoundChannel != NULL) && gRecordSound && (gVideoChannel != NULL) && gRecordVideo && gSplitTracks) { |
myErr = QTCap_SetTrackFile(gVideoChannel, kVideoSavePrompt, kVideoSaveMovieFileName); |
if (myErr != noErr) |
goto bail; |
myErr = QTCap_SetTrackFile(gSoundChannel, kSoundSavePrompt, kSoundSaveMovieFileName); |
if (myErr != noErr) |
goto bail; |
} |
// if not recording sound or video, then disable those channels |
if ((gSoundChannel != NULL) && !gRecordSound) |
SGSetChannelUsage(gSoundChannel, 0); |
if ((gVideoChannel != NULL) && !gRecordVideo) |
SGSetChannelUsage(gVideoChannel, 0); |
// attempt to recover the preview area obscured by dialogs |
#if TARGET_OS_WIN32 |
UpdatePort(gMonitor); |
#endif |
SGUpdate(gSeqGrabber, 0); |
// create a movie file for the destination movie |
myErr = CreateMovieFile(&myFile, sigMoviePlayer, smSystemScript, myFlags, NULL, NULL); |
if (myErr != noErr) |
goto bail; |
FlushEvents(mDownMask + mUpMask, 0); |
// record until the user clicks the mouse button |
myErr = SGStartRecord(gSeqGrabber); |
if (myErr != noErr) |
goto bail; |
while (!Button() && (myErr == noErr)) |
myErr = SGIdle(gSeqGrabber); |
// if we recorded until we ran out of space, then allow SGStop to be called to write the movie resource; |
// the assumption here is that the data output filled up but the disk has enough free space left to |
// write the movie resource |
if (!((myErr == dskFulErr) || (myErr != eofErr))) |
goto bail; |
// stop the recording that's currently happening |
myErr = SGStop(gSeqGrabber); |
SGStartPreview(gSeqGrabber); |
bail: |
free(myPrompt); |
free(myFileName); |
if (myErr == noErr) |
return; |
SGPause(gSeqGrabber, false); |
SGStartPreview(gSeqGrabber); |
} |
////////// |
// |
// QTCap_SGModalFilterProc |
// Handle events while a Sequence Grabber settings dialog box is displayed. |
// |
// The theRefCon parameter is expected to be the DialogPtr of the monitor window. |
// |
////////// |
#if TARGET_OS_MAC |
static PASCAL_RTN Boolean QTCap_SGModalFilterProc (DialogPtr theDialog, const EventRecord *theEvent, short *theItemHit, long theRefCon) |
{ |
Boolean myEventHandled = false; |
WindowPtr myWindow = NULL; |
RgnHandle myWindowRgn = NULL; |
GrafPtr mySavedPort; |
Rect myRect; |
DialogPtr myMonitor = (DialogPtr)theRefCon; |
switch (theEvent->what) { |
case updateEvt: |
// find out which window needs to be updated |
myWindow = (WindowPtr)theEvent->message; |
if (myWindow == GetDialogWindow(myMonitor)) { |
// update the monitor window, using the stored picture |
GetPort(&mySavedPort); |
MacSetPort(GetWindowPort(myWindow)); |
#if TARGET_API_MAC_CARBON |
GetPortBounds(GetDialogPort(myMonitor), &myRect); |
#else |
myRect = myWindow->portRect; |
#endif |
// draw the saved monitor picture into the monitor window |
if (gMonitorPICT != NULL) |
DrawPicture(gMonitorPICT, &myRect); |
// clear the update region |
BeginUpdate(myWindow); |
EndUpdate(myWindow); |
MacSetPort(mySavedPort); |
myEventHandled = true; |
} else if ((myWindow != NULL) && (myWindow != GetDialogWindow(theDialog))) { |
// update the specified window, if it's behind the modal dialog box |
QTFrame_HandleEvent((EventRecord *)theEvent); |
myEventHandled = false; // so sayeth IM |
} |
break; |
case nullEvent: |
// do idle-time processing for all open windows in our window list |
if (gAppInForeground) |
QTFrame_IdleMovieWindows(); |
myEventHandled = false; |
break; |
default: |
myEventHandled = false; |
break; |
} |
// let the OS's standard filter proc handle the event, if it hasn't already been handled |
if (gHasNewDialogCalls && (myEventHandled == false)) |
myEventHandled = StdFilterProc(theDialog, (EventRecord *)theEvent, theItemHit); |
return(myEventHandled); |
} |
#endif |
////////// |
// |
// QTCap_GetVideoSettings |
// Display the video settings dialog box and get the new settings. |
// |
////////// |
void QTCap_GetVideoSettings (void) |
{ |
Rect myNewActiveVideoRect; |
short myWidth, myHeight; |
GrafPtr mySavedPort; |
SGModalFilterUPP myFilterUPP = NULL; |
Rect myRect; |
ComponentResult myErr = noErr; |
// get our current state |
GetPort(&mySavedPort); |
// pause previewing |
SGPause(gSeqGrabber, true); |
// display the video setting dialog box |
myErr = QTCap_GetChannelSettings(gVideoChannel); |
if (myErr != noErr) |
goto bail; |
// retrieve the user's choices |
SGGetSrcVideoBounds(gVideoChannel, &myNewActiveVideoRect); |
// set up our port |
SetPortDialogPort(gMonitor); |
// has our active rectangle changed? |
// if so, it's because our video standard changed (e.g., NTSC to PAL) and we need to adjust our monitor window |
if (!MacEqualRect(&gActiveVideoRect, &myNewActiveVideoRect)) { |
short myDivisor = 1; // assume gFullSize |
if (gQuarterSize) |
myDivisor = 4; |
else if (gHalfSize) |
myDivisor = 2; |
myWidth = (myNewActiveVideoRect.right - myNewActiveVideoRect.left) / myDivisor; |
myHeight = (myNewActiveVideoRect.bottom - myNewActiveVideoRect.top) / myDivisor; |
gActiveVideoRect = myNewActiveVideoRect; |
SizeWindow(GetDialogWindow(gMonitor), myWidth, myHeight, false); |
GetPortBounds(GetDialogPort(gMonitor), &myRect); |
SGSetChannelBounds(gVideoChannel, &myRect); |
} |
bail: |
MacSetPort(mySavedPort); |
#if !TARGET_OS_MAC |
// this is necessary, for now, to get the grab to start again after the dialog goes away; |
// for some reason the video destRect never gets reset to point back to the monitor window |
SGSetChannelBounds(gVideoChannel, &(gMonitor->portRect)); |
#endif |
// restart previewing |
SGPause(gSeqGrabber, false); |
} |
////////// |
// |
// QTCap_GetSoundSettings |
// Display the sound settings dialog box. |
// |
////////// |
void QTCap_GetSoundSettings (void) |
{ |
QTCap_GetChannelSettings(gSoundChannel); |
} |
////////// |
// |
// QTCap_GetChannelSettings |
// Display the settings dialog box for the specified channel. |
// |
////////// |
static ComponentResult QTCap_GetChannelSettings (SGChannel theChannel) |
{ |
SGModalFilterUPP myFilterUPP = NULL; |
ComponentResult myErr = noErr; |
// get rid of any existing monitor picture |
if (gMonitorPICT != NULL) { |
KillPicture(gMonitorPICT); |
gMonitorPICT = NULL; |
} |
// get the picture currently in the monitor window |
SGGrabPict(gSeqGrabber, &gMonitorPICT, NULL, 0, grabPictOffScreen); |
// display the settings dialog box |
#if TARGET_OS_MAC |
myFilterUPP = NewSGModalFilterUPP(QTCap_SGModalFilterProc); |
#endif |
myErr = SGSettingsDialog(gSeqGrabber, theChannel, 0, NULL, 0L, myFilterUPP, (long)gMonitor); |
#if TARGET_OS_MAC |
DisposeSGModalFilterUPP(myFilterUPP); |
#endif |
// get rid of the monitor picture |
if (gMonitorPICT != NULL) { |
KillPicture(gMonitorPICT); |
gMonitorPICT = NULL; |
} |
return(myErr); |
} |
////////// |
// |
// QTCap_ResizeMonitorWindow |
// Resize the monitor window to the specified size. |
// |
////////// |
void QTCap_ResizeMonitorWindow (short theDivisor) |
{ |
Rect myRect; |
short myWidth, myHeight; |
GrafPtr mySavedPort; |
ComponentResult myErr = noErr; |
// calculate the new width and height |
myWidth = (gActiveVideoRect.right - gActiveVideoRect.left) / theDivisor; |
myHeight = (gActiveVideoRect.bottom - gActiveVideoRect.top) / theDivisor; |
gQuarterSize = (theDivisor == 4); |
gHalfSize = (theDivisor == 2); |
gFullSize = (theDivisor == 1); |
// resize the monitor window |
GetPort(&mySavedPort); |
SetPortDialogPort(gMonitor); |
SGPause(gSeqGrabber, true); |
SizeWindow(GetDialogWindow(gMonitor), myWidth, myHeight, false); |
GetPortBounds(GetDialogPort(gMonitor), &myRect); |
SGSetChannelBounds(gVideoChannel, &myRect); |
MacSetPort(mySavedPort); |
SGPause(gSeqGrabber, false); |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14