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.
SimpleVideoOut.c
/* |
File: SimpleVideoOut.c |
Description: SimpleVideoOut demonstrates how QuickTime Video Output Components can be |
used to play video out to hardware. A common use would be playing a dv stream |
(.dv file) out QuickTime's FireWire Video Output Component and recording it |
on your handy dandy DV camera. |
Author: QuickTime DTS |
Version: 2.0.6 |
Copyright: © Copyright 2000 - 2005 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): <6> 07/15/03 added oDoc and respect the highQuality hint to |
make jmb happy and added Close to make gd happy |
<5> 09/25/02 fixed Clock UI to always reflect correct state |
<4> 06/14/02 added ability to turn VOut off, minor UI changes |
<3> 11/16/01 initial release version 2.0 |
<2> 07/27/01 carbonized & updated for QT5 |
<1> 01/28/00 initial release version 1.0 |
*/ |
#define TARGET_API_MAC_CARBON 1 |
#if __APPLE_CC__ || __MACH__ |
#include <Carbon/Carbon.h> |
#include <QuickTime/QuickTime.h> |
#else |
#include <Carbon.h> |
#endif |
#include <new> // for std::nothrow |
#include "CVideoOutput.h" |
using namespace dts; |
const short kOSXMenuBar = 128; |
const short kOS9MenuBar = 129; |
const short kFileMenuIDX = 130; |
const short kFileMenuID9 = 131; |
const short kAlert = 128; |
const short kAboutBox = 129; |
const short kPopupMenu = 129; |
const short kOpenID = 1; |
const short kCloseID = 2; |
const short kEchoOnID = 1; |
const short kEchoOffID = 2; |
const short kVOSoundOnID = 4; |
const short kVOSoundOffID = 5; |
const short kVOClockID = 7; |
const short kDefaultClockID = 8; |
const short kVOutOnID = 10; |
const short kVOutOffID = 11; |
const short kHighQOnID = 13; |
const short kHighQOffID = 14; |
const short kVOSelectID = 16; |
const EventTime kMCIdleDuration = kEventDurationSecond / 30; |
typedef struct { |
WindowRef theWindow; |
Movie theMovie; |
MovieController theController; |
CVideoOutput *pVideoOutput; |
MenuRef thePopupMenuRef; |
short theMCHeight; |
EventLoopTimerRef theTimerRef; |
} WindowDataRecord, *WindowDataRecordPtr; |
// Globals |
static WindowDataRecord gGlobals = { NULL }; |
static BitMap gScreenBits; |
static short gFileMenuID = 0; |
int main( void ); |
void Initialize( void ); |
OSErr DoOpen( ConstFSSpecPtr inFSSpecPtr, WindowDataRecordPtr inUserDataPtr ); |
OSErr StartVideoOutput( WindowDataRecordPtr inUserDataPtr ); |
OSErr DoOpenMovieFromFile( ConstFSSpecPtr inFSSpecPtr, WindowDataRecordPtr inUserDataPtr ); |
OSErr DoCreateMovieController( WindowDataRecordPtr inUserDataPtr ); |
void DoError( const unsigned char inErrorText[] ); |
Boolean IsHighQualityOn( Movie inMovie ); |
void SetMCEchoOffWindowSize( WindowDataRecordPtr inUserDataPtr ); |
void SetMCResizeBounds( WindowDataRecordPtr inUserDataPtr, Boolean inResizeable ); |
void SetMCPopupMenuState( WindowDataRecordPtr inUserDataPtr, short inState ); |
/* HandleAEOpenApplication |
Open Application Event handler |
*/ |
static pascal OSErr HandleAEOpenApplication( const AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon ) |
{ |
#pragma unused(theAppleEvent, reply, handlerRefcon) |
return ( DoOpen( NULL, &gGlobals ) ); |
} |
/* HandleAEOpenDocument |
Open Document Apple Event handler |
*/ |
static pascal OSErr HandleAEOpenDocuments( const AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon ) |
{ |
#pragma unused(reply, handlerRefcon) |
AEDescList theDocList; |
AEKeyword theKeyWord; |
DescType theTypeCode; |
FSSpec theFSSpec; |
long theActualSize; |
long numItems = 0; |
OSErr err = noErr; |
// only one document at a time |
if ( gGlobals.theWindow ) return paramErr; |
// get the direct parameter and put it into theDocList |
theDocList.dataHandle = NULL; |
err = AEGetParamDesc( theAppleEvent, keyDirectObject, typeAEList, &theDocList ); |
// count the descriptor records in the list |
if ( noErr == err ) |
err = AECountItems( &theDocList, &numItems ); |
// need at least one, cuz we only grab the first |
if ( numItems > 0 ) { |
err = AEGetNthPtr( &theDocList, 1, typeFSS, &theKeyWord, &theTypeCode, (Ptr)&theFSSpec, sizeof(FSSpec), &theActualSize ); |
if ( noErr == err ) { |
Boolean canOpenAsMovie; |
err = CanQuickTimeOpenFile( &theFSSpec, 0, 0, NULL, &canOpenAsMovie, NULL, 0 ); |
if ( noErr == err && true == canOpenAsMovie ) { |
err = DoOpen( &theFSSpec, &gGlobals ); |
} |
} |
} |
if ( theDocList.dataHandle ) |
AEDisposeDesc( &theDocList ); |
return err; |
} |
/* HandleAEQuit |
Quit Apple Event handler |
*/ |
static pascal OSErr HandleAEQuit( const AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon ) |
{ |
#pragma unused(theAppleEvent, reply, handlerRefcon) |
HICommand theCommand = { kEventAttributeNone, kHICommandQuit }; |
ProcessHICommand( &theCommand ); |
return noErr; |
} |
/* myMCActionFilterWithRefConProc |
Movie controller filter proc, this filter handles the custom button and size changed actions. |
*/ |
static pascal Boolean myMCActionFilterWithRefConProc( MovieController theMC, short theAction, void *theParams, long theRefCon ) |
{ |
#pragma unused(theParams) |
Boolean isHandled = false; |
WindowDataRecordPtr pUserData = (WindowDataRecordPtr)theRefCon; |
if ( pUserData == NULL ) return isHandled; |
switch ( theAction ) { |
case mcActionCustomButtonClick: |
{ |
Rect theWindowBoundsRect, theMCBoundsRect; |
// Don't want MCIdle calls while in here |
SetEventLoopTimerNextFireTime( pUserData->theTimerRef, kEventDurationForever ); |
// Calculate the position for the CustomButton pop-up menu |
RgnHandle theRgn = NewRgn(); |
GetWindowRegion( pUserData->theWindow, kWindowStructureRgn, theRgn ); |
GetRegionBounds( theRgn, &theWindowBoundsRect ); |
DisposeRgn( theRgn ); |
MCGetControllerBoundsRect( theMC, &theMCBoundsRect ); |
LocalToGlobal( (Point *)&theMCBoundsRect.bottom ); |
// Handle the menu selections and settings |
long theResult = PopUpMenuSelect( pUserData->thePopupMenuRef, theMCBoundsRect.bottom - pUserData->theMCHeight, theWindowBoundsRect.right + 1, 0 ); |
short theItem = LoWord( theResult ); |
switch ( theItem ) { |
case kEchoOnID: |
MCSetControllerAttached( theMC, true ); |
pUserData->pVideoOutput->SetEchoPort( GetWindowPort(pUserData->theWindow) ); |
MCMovieChanged( theMC, pUserData->theMovie ); |
SetMCResizeBounds( pUserData, true ); |
MCDoAction( theMC, mcActionControllerSizeChanged, 0 ); |
SetMCPopupMenuState( pUserData, kEchoOnID ); |
break; |
case kEchoOffID: |
MCSetControllerAttached( theMC, false ); |
MCSetControllerPort( theMC, GetWindowPort( pUserData->theWindow ) ); |
SetMCResizeBounds( pUserData, false ); |
// resize the controller and the window to our echo off size |
SetMCEchoOffWindowSize( pUserData ); |
pUserData->pVideoOutput->SetEchoPort(); |
SetMCPopupMenuState( pUserData, kEchoOffID ); |
break; |
case kVOSoundOnID: |
pUserData->pVideoOutput->SetSoundDevice(); |
// must set the clock after selecting the sound device |
pUserData->pVideoOutput->SetClock(); |
SetMCPopupMenuState( pUserData, kVOSoundOnID ); |
break; |
case kVOSoundOffID: |
pUserData->pVideoOutput->SetSoundDevice( false ); |
// must set the clock after selecting the sound device |
pUserData->pVideoOutput->SetClock( false ); |
SetMCPopupMenuState( pUserData, kVOSoundOffID ); |
break; |
case kVOClockID: |
pUserData->pVideoOutput->SetClock(); |
SetMCPopupMenuState( pUserData, kVOClockID ); |
break; |
case kDefaultClockID: |
pUserData->pVideoOutput->SetClock( false ); |
SetMCPopupMenuState( pUserData, kDefaultClockID ); |
break; |
case kVOutOnID: |
MCDoAction( theMC, mcActionPlay, 0 ); |
MCSetControllerAttached( theMC, true ); |
StartVideoOutput( pUserData ); |
SetMCPopupMenuState( pUserData, kVOutOnID ); |
break; |
case kVOutOffID: |
MCDoAction( theMC, mcActionPlay, 0 ); |
MCSetControllerAttached( theMC, true ); |
pUserData->pVideoOutput->SetEchoPort( GetWindowPort(pUserData->theWindow) ); |
SetMCResizeBounds( pUserData, true ); |
MCDoAction( theMC, mcActionControllerSizeChanged, 0 ); |
MCMovieChanged( theMC, pUserData->theMovie ); |
pUserData->pVideoOutput->End(); |
SetMCPopupMenuState( pUserData, kVOutOffID ); |
break; |
case kHighQOnID: |
SetMoviePlayHints( pUserData->theMovie, hintsHighQuality, hintsHighQuality ); |
SetMCPopupMenuState( pUserData, kHighQOnID ); |
break; |
case kHighQOffID: |
SetMoviePlayHints( pUserData->theMovie, 0, hintsHighQuality ); |
SetMCPopupMenuState( pUserData, kHighQOffID ); |
break; |
case kVOSelectID: |
MCDoAction( theMC, mcActionPlay, 0 ); |
MCSetControllerAttached( theMC, true ); |
MCDoAction( theMC, mcActionControllerSizeChanged, 0 ); |
// make sure to change the movies GWorld back to a valid window |
// before shutting down the video output or bad things could happen |
// if the VO component doesn't support echo this call translates to SetMovieGWorld() |
pUserData->pVideoOutput->SetEchoPort( GetWindowPort(pUserData->theWindow) ); |
MCMovieChanged( theMC, pUserData->theMovie ); |
pUserData->pVideoOutput->Close(); |
pUserData->pVideoOutput->SelectVideoOutputComponent(); |
pUserData->pVideoOutput->Open(); |
if ( pUserData->pVideoOutput->GetError() || StartVideoOutput( pUserData ) ) { return false; } |
break; |
default: |
break; |
} // switch |
SetEventLoopTimerNextFireTime( pUserData->theTimerRef, kMCIdleDuration ); |
isHandled = true; |
break; |
} |
case mcActionControllerSizeChanged: |
Rect theMCBoundsRect; |
MCGetControllerBoundsRect( theMC, &theMCBoundsRect ); |
SizeWindow( pUserData->theWindow, theMCBoundsRect.right, theMCBoundsRect.bottom, true ); |
AlignWindow( pUserData->theWindow, false, NULL, NULL ); |
break; |
default: |
break; |
} // switch |
return isHandled; |
} |
/* myMovieControllerIdleTimer |
Idle timer to give time to the MovieController. |
*/ |
static pascal void myMovieControllerIdleTimer(EventLoopTimerRef inTimer, void *inUserData) |
{ |
#pragma unused(inTimer) |
WindowDataRecordPtr pUserData = (WindowDataRecordPtr)inUserData; |
if ( pUserData->theController == NULL ) return; |
MCIdle( pUserData->theController ); |
} |
/* myWindowEventHandler |
Carbon event handler for the window - This handler gives events over to |
MCIsPlayerEvent so QuickTime can do all the work for us. |
*/ |
static pascal OSStatus myWindowEventHandler(EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData) |
{ |
#pragma unused(inHandlerCallRef) |
UInt32 theEventKind = GetEventKind(inEvent); |
OSStatus status = eventNotHandledErr; |
static Fixed thePlayRate = 0; |
WindowDataRecordPtr pUserData = (WindowDataRecordPtr)inUserData; |
if ( pUserData == NULL ) return status; |
switch ( theEventKind ) { |
case kEventWindowClose: |
MCDoAction( pUserData->theController, mcActionPlay, 0 ); |
MCSetControllerAttached( pUserData->theController, true ); |
// TransitionWindow will cause us to be re-entered with kEventWindowDeactivated |
// so don't toss anything needed (like the TimerRef) before this call. |
TransitionWindow( pUserData->theWindow, kWindowZoomTransitionEffect, kWindowHideTransitionAction, NULL ); |
// make sure to change the movies GWorld back to the window |
// before shutting down the video output or bad things could happen |
pUserData->pVideoOutput->SetEchoPort( GetWindowPort(pUserData->theWindow) ); |
MCMovieChanged( pUserData->theController, pUserData->theMovie ); |
pUserData->pVideoOutput->End(); |
RemoveEventLoopTimer( pUserData->theTimerRef ); |
DisposeMovieController( pUserData->theController ); |
DisposeMovie( pUserData->theMovie ); |
ReleaseWindow( pUserData->theWindow ); |
pUserData->theWindow = NULL; |
pUserData->theMovie = NULL; |
pUserData->theController = NULL; |
pUserData->theMCHeight = 0; |
pUserData->theTimerRef = NULL; |
status = noErr; |
break; |
case kEventWindowActivated: |
MCDoAction( pUserData->theController, mcActionPlay, (Ptr)thePlayRate ); |
SetEventLoopTimerNextFireTime( pUserData->theTimerRef, kMCIdleDuration ); |
goto passEventToController; |
case kEventWindowDeactivated: |
MCDoAction( pUserData->theController, mcActionGetPlayRate, &thePlayRate ); |
MCDoAction( pUserData->theController, mcActionPlay, 0 ); |
SetEventLoopTimerNextFireTime( pUserData->theTimerRef, kEventDurationForever ); |
// fall through |
passEventToController: |
default: |
EventRecord theEvent; |
ConvertEventRefToEventRecord( inEvent, &theEvent ); |
if ( MCIsPlayerEvent( pUserData->theController, &theEvent )) status = noErr; |
break; |
} |
return status; |
} |
/* myApplicationEventHandler |
Main application carbon event handler takes care of the HI commands and menu commands. |
*/ |
static pascal OSStatus myApplicationEventHandler( EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData ) |
{ |
#pragma unused(inHandlerCallRef) |
UInt32 theEventClass = GetEventClass( inEvent ); |
UInt32 theEventKind = GetEventKind( inEvent ); |
OSStatus status = eventNotHandledErr; |
static Fixed thePlayRate = 0; |
WindowDataRecordPtr pUserData = (WindowDataRecordPtr)inUserData; |
if ( pUserData == NULL ) return status; |
switch ( theEventClass ) { |
case kEventClassCommand: |
if ( kEventCommandProcess == theEventKind ) { |
HICommand theCommand; |
GetEventParameter( inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &theCommand ); |
switch ( theCommand.commandID ) { |
case kHICommandAbout: |
Alert( kAboutBox, NULL ); |
status = noErr; |
break; |
case kHICommandOpen: |
status = DoOpen( NULL, pUserData ); |
break; |
case kHICommandClose: |
// If the window is open tell it to close |
if ( pUserData->theWindow ) { |
EventRef theWindowCloseEvent; |
CreateEvent( NULL, kEventClassWindow, kEventWindowClose, 0, kEventAttributeNone, &theWindowCloseEvent ); |
SendEventToEventTarget( theWindowCloseEvent, GetWindowEventTarget( pUserData->theWindow ) ); |
ReleaseEvent( theWindowCloseEvent ); |
} |
status = noErr; |
break; |
case kHICommandQuit: |
{ // gcc complains without this in brackets |
HICommand theCommand = { kEventAttributeNone, kHICommandClose }; |
ProcessHICommand( &theCommand ); |
QuitApplicationEventLoop(); |
status = noErr; |
break; |
} |
default: |
break; |
} |
} |
break; |
case kEventClassMenu: |
{ // gcc complains without this in brackets |
MenuRef theMenuRef = NULL; |
GetEventParameter( inEvent, kEventParamDirectObject, typeMenuRef, NULL, sizeof(MenuRef), NULL, &theMenuRef ); |
switch ( theEventKind ) { |
case kEventMenuBeginTracking: |
if ( pUserData->theController ) { |
// If the movie is playing, stop it during menu tracking then start it |
// up again or bad things can happen under 9 |
MCDoAction( pUserData->theController, mcActionGetPlayRate, &thePlayRate ); |
MCDoAction( pUserData->theController, mcActionPlay, 0 ); |
SetEventLoopTimerNextFireTime( pUserData->theTimerRef, kEventDurationForever ); |
status = noErr; |
} |
break; |
case kEventMenuEndTracking: |
if ( pUserData->theController ) { |
MCDoAction( pUserData->theController, mcActionPlay, (Ptr)thePlayRate ); |
SetEventLoopTimerNextFireTime( pUserData->theTimerRef, kMCIdleDuration ); |
status = noErr; |
} |
break; |
case kEventMenuEnableItems: |
if ( GetMenuRef( gFileMenuID ) == theMenuRef ) { |
WindowRef theDocumentWindowRef = GetFrontWindowOfClass( kDocumentWindowClass, true ); |
WindowRef theFrontNonFloatingWindowRef = FrontNonFloatingWindow(); |
if ( pUserData->theWindow && |
pUserData->theWindow == theDocumentWindowRef && |
pUserData->theWindow == theFrontNonFloatingWindowRef ) { |
DisableMenuItem( theMenuRef, kOpenID ); |
EnableMenuItem( theMenuRef, kCloseID ); |
} else if ( theFrontNonFloatingWindowRef == NULL ) { |
EnableMenuItem( theMenuRef, kOpenID ); |
DisableMenuItem( theMenuRef, kCloseID ); |
} else { |
DisableMenuItem( theMenuRef, 0 ); |
} |
status = noErr; |
} |
break; |
} |
break; |
} |
default: |
break; |
} |
return status; |
} |
#pragma mark- |
/* DoError |
Display error alert |
*/ |
void DoError( const unsigned char inErrorText[] ) |
{ |
ParamText( inErrorText, NULL, NULL, NULL ); |
StopAlert( kAlert, NULL ); |
} |
/* IsHighQualityOn |
Figure out if any visual tracks in the movie were saved with the |
High Quality hint on. If so turn on the HighQuality play hint for the movie. |
*/ |
Boolean IsHighQualityOn( Movie inMovie ) |
{ |
Track theTrack; |
UInt8 theCount; |
Boolean rc = false; |
theCount = GetMovieTrackCount( inMovie ); |
while ( theCount ) { |
// get an enabled visual track |
theTrack = GetMovieIndTrackType( inMovie, theCount--, VisualMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly ); |
if ( theTrack ) { |
long theDefaultHints; |
GetTrackLoadSettings( theTrack, NULL, NULL, NULL, &theDefaultHints ); |
if ( !GetMoviesError() ) |
if ( theDefaultHints & hintsHighQuality ) { |
rc = true; |
break; |
} |
} |
} |
return rc; |
} |
/* SetMCEchoOffWindowSize |
When echo is off there's no need to show an empty window so, resize |
the controller to a fixed size and size the window appropriately. |
*/ |
void SetMCEchoOffWindowSize( WindowDataRecordPtr inUserDataPtr ) |
{ |
Rect theMCBoundsRect = { 0, 0, inUserDataPtr->theMCHeight, 320 }; |
MCSetControllerBoundsRect( inUserDataPtr->theController, &theMCBoundsRect ); |
MCDoAction( inUserDataPtr->theController, mcActionControllerSizeChanged, 0 ); |
} |
/* SetMCResizeBounds |
Turn on/off the movie controllers ability to resize. |
*/ |
void SetMCResizeBounds( WindowDataRecordPtr inUserDataPtr, Boolean inResizeable ) |
{ |
Rect theResizeBounds = { 0, 0, 0, 0 }; // if inResizeable is false use the empty rect to turn it off |
if ( inResizeable ) { |
// we can resize from full size down to half size |
GetMovieNaturalBoundsRect( inUserDataPtr->theMovie, &theResizeBounds ); |
theResizeBounds.top = theResizeBounds.bottom / 2; |
theResizeBounds.left = theResizeBounds.right / 2; |
} |
MCDoAction( inUserDataPtr->theController, mcActionSetGrowBoxBounds, &theResizeBounds); |
} |
/* SetMCPopupMenuState |
Make sure the menu items are in sync with the video output settings. |
*/ |
void SetMCPopupMenuState( WindowDataRecordPtr inUserDataPtr, short inState ) |
{ |
switch (inState) { |
case 0: // default state |
{ |
if ( inUserDataPtr->pVideoOutput->CanDoEchoPort() ) { |
// SimpleVideoOut always set an echo port if the VO can do it |
CheckMenuItem( inUserDataPtr->thePopupMenuRef, kEchoOnID, true ); |
CheckMenuItem ( inUserDataPtr->thePopupMenuRef, kEchoOffID, false ); |
DisableMenuItem( inUserDataPtr->thePopupMenuRef, kEchoOnID); |
EnableMenuItem( inUserDataPtr->thePopupMenuRef, kEchoOffID); |
} else { |
CheckMenuItem( inUserDataPtr->thePopupMenuRef, kEchoOnID, false ); |
CheckMenuItem ( inUserDataPtr->thePopupMenuRef, kEchoOffID, true ); |
DisableMenuItem( inUserDataPtr->thePopupMenuRef, kEchoOnID); |
DisableMenuItem( inUserDataPtr->thePopupMenuRef, kEchoOffID); |
} |
if ( inUserDataPtr->pVideoOutput->HasSoundOutput() ) { |
// Begin() by default uses the VO sound output if there is one |
CheckMenuItem( inUserDataPtr->thePopupMenuRef, kVOSoundOnID, true ); |
CheckMenuItem( inUserDataPtr->thePopupMenuRef, kVOSoundOffID, false ); |
DisableMenuItem( inUserDataPtr->thePopupMenuRef, kVOSoundOnID); |
EnableMenuItem( inUserDataPtr->thePopupMenuRef, kVOSoundOffID); |
} else { |
CheckMenuItem( inUserDataPtr->thePopupMenuRef, kVOSoundOnID, false ); |
CheckMenuItem( inUserDataPtr->thePopupMenuRef, kVOSoundOffID, true ); |
DisableMenuItem( inUserDataPtr->thePopupMenuRef, kVOSoundOnID); |
DisableMenuItem( inUserDataPtr->thePopupMenuRef, kVOSoundOffID); |
} |
if ( inUserDataPtr->pVideoOutput->HasClock() ) { |
// Begin() by default makes sure to use the VO clock if there is one |
CheckMenuItem( inUserDataPtr->thePopupMenuRef, kVOClockID, true ); |
CheckMenuItem( inUserDataPtr->thePopupMenuRef, kDefaultClockID, false ); |
DisableMenuItem( inUserDataPtr->thePopupMenuRef, kVOClockID); |
EnableMenuItem( inUserDataPtr->thePopupMenuRef, kDefaultClockID); |
} else { |
CheckMenuItem( inUserDataPtr->thePopupMenuRef, kVOClockID, false ); |
CheckMenuItem( inUserDataPtr->thePopupMenuRef, kDefaultClockID, true ); |
DisableMenuItem( inUserDataPtr->thePopupMenuRef, kVOClockID); |
DisableMenuItem( inUserDataPtr->thePopupMenuRef, kDefaultClockID); |
} |
// Video Output On by default |
CheckMenuItem( inUserDataPtr->thePopupMenuRef, kVOutOnID, true ); |
CheckMenuItem( inUserDataPtr->thePopupMenuRef, kVOutOffID, false ); |
EnableMenuItem( inUserDataPtr->thePopupMenuRef, kVOutOffID ); |
DisableMenuItem( inUserDataPtr->thePopupMenuRef, kVOutOnID ); |
// High Quality Off by default |
CheckMenuItem( inUserDataPtr->thePopupMenuRef, kHighQOnID, false ); |
CheckMenuItem( inUserDataPtr->thePopupMenuRef, kHighQOffID, true ); |
EnableMenuItem( inUserDataPtr->thePopupMenuRef, kHighQOnID ); |
DisableMenuItem( inUserDataPtr->thePopupMenuRef, kHighQOffID ); |
break; |
} |
case kEchoOnID: |
CheckMenuItem( inUserDataPtr->thePopupMenuRef, kEchoOnID, true ); |
CheckMenuItem ( inUserDataPtr->thePopupMenuRef, kEchoOffID, false ); |
EnableMenuItem( inUserDataPtr->thePopupMenuRef, kEchoOffID ); |
DisableMenuItem( inUserDataPtr->thePopupMenuRef, kEchoOnID ); |
break; |
case kEchoOffID: |
CheckMenuItem( inUserDataPtr->thePopupMenuRef, kEchoOnID, false ); |
CheckMenuItem ( inUserDataPtr->thePopupMenuRef, kEchoOffID, true ); |
EnableMenuItem( inUserDataPtr->thePopupMenuRef, kEchoOnID ); |
DisableMenuItem( inUserDataPtr->thePopupMenuRef, kEchoOffID ); |
break; |
case kVOSoundOnID: |
CheckMenuItem( inUserDataPtr->thePopupMenuRef, kVOSoundOnID, true ); |
CheckMenuItem( inUserDataPtr->thePopupMenuRef, kVOSoundOffID, false ); |
EnableMenuItem( inUserDataPtr->thePopupMenuRef, kVOSoundOffID ); |
DisableMenuItem( inUserDataPtr->thePopupMenuRef, kVOSoundOnID ); |
if ( inUserDataPtr->pVideoOutput->HasClock() ) { |
CheckMenuItem( inUserDataPtr->thePopupMenuRef, kVOClockID, true ); |
CheckMenuItem( inUserDataPtr->thePopupMenuRef, kDefaultClockID, false ); |
EnableMenuItem( inUserDataPtr->thePopupMenuRef, kDefaultClockID ); |
DisableMenuItem( inUserDataPtr->thePopupMenuRef, kVOClockID ); |
} |
break; |
case kVOSoundOffID: |
CheckMenuItem( inUserDataPtr->thePopupMenuRef, kVOSoundOnID, false ); |
CheckMenuItem( inUserDataPtr->thePopupMenuRef, kVOSoundOffID, true ); |
EnableMenuItem( inUserDataPtr->thePopupMenuRef, kVOSoundOnID ); |
DisableMenuItem( inUserDataPtr->thePopupMenuRef, kVOSoundOffID ); |
if ( inUserDataPtr->pVideoOutput->HasClock() ) { |
CheckMenuItem( inUserDataPtr->thePopupMenuRef, kVOClockID, false ); |
CheckMenuItem( inUserDataPtr->thePopupMenuRef, kDefaultClockID, true ); |
EnableMenuItem( inUserDataPtr->thePopupMenuRef, kVOClockID ); |
DisableMenuItem( inUserDataPtr->thePopupMenuRef, kDefaultClockID ); |
} |
break; |
case kVOClockID: |
CheckMenuItem( inUserDataPtr->thePopupMenuRef, kVOClockID, true ); |
CheckMenuItem( inUserDataPtr->thePopupMenuRef, kDefaultClockID, false ); |
EnableMenuItem( inUserDataPtr->thePopupMenuRef, kDefaultClockID ); |
DisableMenuItem( inUserDataPtr->thePopupMenuRef, kVOClockID ); |
break; |
case kDefaultClockID: |
CheckMenuItem( inUserDataPtr->thePopupMenuRef, kVOClockID, false ); |
CheckMenuItem( inUserDataPtr->thePopupMenuRef, kDefaultClockID, true ); |
EnableMenuItem( inUserDataPtr->thePopupMenuRef, kVOClockID ); |
DisableMenuItem( inUserDataPtr->thePopupMenuRef, kDefaultClockID ); |
break; |
case kVOutOnID: |
CheckMenuItem( inUserDataPtr->thePopupMenuRef, kVOutOnID, true ); |
CheckMenuItem( inUserDataPtr->thePopupMenuRef, kVOutOffID, false ); |
EnableMenuItem( inUserDataPtr->thePopupMenuRef, kVOutOffID ); |
DisableMenuItem( inUserDataPtr->thePopupMenuRef, kVOutOnID ); |
break; |
case kVOutOffID: |
CheckMenuItem( inUserDataPtr->thePopupMenuRef, kVOutOnID, false ); |
CheckMenuItem( inUserDataPtr->thePopupMenuRef, kVOutOffID, true ); |
EnableMenuItem( inUserDataPtr->thePopupMenuRef, kVOutOnID ); |
DisableMenuItem( inUserDataPtr->thePopupMenuRef, kVOutOffID ); |
CheckMenuItem( inUserDataPtr->thePopupMenuRef, kEchoOnID, false ); |
CheckMenuItem( inUserDataPtr->thePopupMenuRef, kEchoOffID, false ); |
CheckMenuItem( inUserDataPtr->thePopupMenuRef, kVOSoundOnID, false ); |
CheckMenuItem( inUserDataPtr->thePopupMenuRef, kVOSoundOffID, false ); |
CheckMenuItem( inUserDataPtr->thePopupMenuRef, kVOClockID, false ); |
CheckMenuItem( inUserDataPtr->thePopupMenuRef, kDefaultClockID, false ); |
DisableMenuItem( inUserDataPtr->thePopupMenuRef, kEchoOnID ); |
DisableMenuItem( inUserDataPtr->thePopupMenuRef, kEchoOffID ); |
DisableMenuItem( inUserDataPtr->thePopupMenuRef, kVOSoundOnID ); |
DisableMenuItem( inUserDataPtr->thePopupMenuRef, kVOSoundOffID ); |
DisableMenuItem( inUserDataPtr->thePopupMenuRef, kVOClockID ); |
DisableMenuItem( inUserDataPtr->thePopupMenuRef, kDefaultClockID ); |
break; |
case kHighQOnID: |
CheckMenuItem( inUserDataPtr->thePopupMenuRef, kHighQOnID, true ); |
CheckMenuItem( inUserDataPtr->thePopupMenuRef, kHighQOffID, false ); |
EnableMenuItem( inUserDataPtr->thePopupMenuRef, kHighQOffID ); |
DisableMenuItem( inUserDataPtr->thePopupMenuRef, kHighQOnID ); |
break; |
case kHighQOffID: |
CheckMenuItem( inUserDataPtr->thePopupMenuRef, kHighQOnID, false ); |
CheckMenuItem( inUserDataPtr->thePopupMenuRef, kHighQOffID, true ); |
EnableMenuItem( inUserDataPtr->thePopupMenuRef, kHighQOnID ); |
DisableMenuItem( inUserDataPtr->thePopupMenuRef, kHighQOffID ); |
break; |
default: |
break; |
} // switch |
} |
/* Initialize |
Yer basic init stuff, if any of this fails we ask for our bill |
and take the express elevator straight to hell - ya, let Bishop go. |
*/ |
void Initialize( void ) |
{ |
EventHandlerRef theEventRef = NULL; |
Handle theMenuBar = NULL; |
const long kVersion10 = 0x00001000; |
// Common Application events we want for 9 and X |
EventTypeSpec theEventTypes[] = { { kEventClassCommand, kEventCommandProcess}, |
{ kEventClassMenu, kEventMenuEnableItems }}; |
// Application events we only want when running on 9 |
EventTypeSpec theEventTypes9[] = { { kEventClassMenu, kEventMenuBeginTracking}, |
{ kEventClassMenu, kEventMenuEndTracking }}; |
long result, systemVersion; |
InitCursor(); |
FlushEvents( everyEvent, 0 ); |
// Check the cell structure |
if ( Gestalt( gestaltQuickTime, &result ) != noErr) { |
DoError( "\pQuickTime is not installed...Doh!" ); |
ExitToShell(); |
} |
// Remember to call EnterMovies even on X! |
EnterMovies(); |
GetQDGlobalsScreenBits( &gScreenBits ); |
result = Gestalt( gestaltSystemVersion, &systemVersion ); |
if ( result == noErr ) { |
// Can I get a menu? |
if ( systemVersion >= kVersion10 ) { |
theMenuBar = GetNewMBar( kOSXMenuBar ); |
gFileMenuID = kFileMenuIDX; |
} else { |
theMenuBar = GetNewMBar( kOS9MenuBar ); |
gFileMenuID = kFileMenuID9; |
} |
if ( theMenuBar ) { |
SetMenuBar( theMenuBar ); |
DrawMenuBar(); |
} else { |
DoError( "\pCould not load menu bar..." ); |
ExitToShell(); |
} |
} else { |
DoError( "\pGestalt gestaltSystemVersion failed..." ); |
ExitToShell(); |
} |
// Get the movie controller CustomPopup menu |
gGlobals.thePopupMenuRef = GetMenu( kPopupMenu ); |
if ( gGlobals.thePopupMenuRef == NULL ) { |
DoError( "\pCould not load menu resource..." ); |
ExitToShell(); |
} |
// Instantiate a VideoOutput object without a specific Movie |
gGlobals.pVideoOutput = new(std::nothrow) CVideoOutput( "\pSimpleVideoOut" ); |
if ( gGlobals.pVideoOutput == NULL || gGlobals.pVideoOutput->GetError() ) { |
DoError( "\pProblem initializing CVideoOutput, or component unavailable..." ); |
ExitToShell(); |
} |
// Install the Apple event handlers |
result = AEInstallEventHandler( kCoreEventClass, kAEOpenApplication, NewAEEventHandlerUPP(HandleAEOpenApplication), 0, false ); |
if ( result ) { DoError( "\pProblem installing kAEOpenApplication Handler..." ); ExitToShell(); } |
result = AEInstallEventHandler( kCoreEventClass, kAEOpenDocuments, NewAEEventHandlerUPP(HandleAEOpenDocuments), 0, false ); |
if ( result ) { DoError( "\pProblem installing kAEOpenDocuments Handler..." ); ExitToShell(); } |
result = AEInstallEventHandler( kCoreEventClass, kAEQuitApplication, NewAEEventHandlerUPP(HandleAEQuit), 0, false ); |
if ( result ) { DoError( "\pProblem installing kAEQuitApplication Handler..." ); ExitToShell(); } |
if ( InstallStandardEventHandler( GetApplicationEventTarget() )) { DoError( "\pProblem installing Standard App. Event Handler..." ); ExitToShell(); } |
if ( InstallApplicationEventHandler( NewEventHandlerUPP( myApplicationEventHandler ), GetEventTypeCount( theEventTypes ), theEventTypes, &gGlobals, &theEventRef )) { |
DoError( "\pProblem installing Application Event Handler..." ); |
ExitToShell(); |
} |
if ( systemVersion < kVersion10 ) { |
if ( AddEventTypesToHandler( theEventRef, GetEventTypeCount( theEventTypes9 ), theEventTypes9 )) { DoError( "\pProblem adding OS9 Carbon Events..." ); ExitToShell(); } |
} |
} |
/* DoOpenMovieFromFile |
This function Opens a selected movie and creates a window. |
*/ |
OSErr DoOpenMovieFromFile( ConstFSSpecPtr inFSSpecPtr, WindowDataRecordPtr inUserDataPtr ) |
{ |
Rect theWindowRect = { 50, 20, 300, 300 }; |
Rect theMovieBox; |
short theMovieRefNum; |
OSErr rc = noErr; |
// Create a new window for the movie |
rc = CreateNewWindow( kDocumentWindowClass, kWindowCloseBoxAttribute, &theWindowRect, &(inUserDataPtr->theWindow) ); |
if ( inUserDataPtr->theWindow == NULL ) rc = errInvalidWindowRef; |
if ( rc ) goto bail; |
SetWTitle( inUserDataPtr->theWindow, inFSSpecPtr->name ); |
SetPortWindowPort( inUserDataPtr->theWindow ); |
// Open the movie file |
rc = OpenMovieFile( inFSSpecPtr, &theMovieRefNum, fsRdPerm ); |
if ( rc ) goto bail; |
rc = NewMovieFromFile( &(inUserDataPtr->theMovie), theMovieRefNum, NULL, NULL, newMovieActive, NULL ); |
CloseMovieFile( theMovieRefNum ); |
if ( rc ) goto bail; |
GetMovieNaturalBoundsRect( inUserDataPtr->theMovie, &theMovieBox ); |
OffsetRect( &theMovieBox, -theMovieBox.left, -theMovieBox.top ); |
SetMovieBox( inUserDataPtr->theMovie, &theMovieBox ); |
// Tell the output component about the movie |
inUserDataPtr->pVideoOutput->SetMovie( inUserDataPtr->theMovie ); |
bail: |
return rc; |
} |
/* DoCreateMovieController |
This function creates a movie controller and sizes the window appropriately, |
it also installs the windows event handlers, shows the window, installs |
the MCAction filter and finally installs the MCIdle carbon timer. |
*/ |
OSErr DoCreateMovieController( WindowDataRecordPtr inUserDataPtr ) |
{ |
EventTypeSpec theEventTypes[] = { { kEventClassMouse, kEventMouseDown }, |
{ kEventClassMouse, kEventMouseUp }, |
{ kEventClassKeyboard, kEventRawKeyDown }, |
{ kEventClassKeyboard, kEventRawKeyRepeat }, |
{ kEventClassKeyboard, kEventRawKeyUp }, |
{ kEventClassWindow, kEventWindowUpdate }, |
{ kEventClassWindow, kEventWindowActivated }, |
{ kEventClassWindow, kEventWindowDeactivated }, |
{ kEventClassWindow, kEventWindowClose } }; |
Rect theMovieBox; |
long mcFlags; |
OSErr rc = noErr; |
GetMovieBox( inUserDataPtr->theMovie, &theMovieBox ); |
inUserDataPtr->theMCHeight = theMovieBox.bottom; |
// Create the movie controller |
inUserDataPtr->theController = NewMovieController( inUserDataPtr->theMovie, &theMovieBox, mcTopLeftMovie ); |
if ( inUserDataPtr->theController == NULL) { rc = illegalControllerOSErr; goto bail; } |
// Enable keys, give us a custom button and turn off stuff |
MCDoAction( inUserDataPtr->theController, mcActionSetKeysEnabled, (Ptr)true ); |
MCDoAction( inUserDataPtr->theController, mcActionGetFlags, &mcFlags ); |
MCDoAction( inUserDataPtr->theController, mcActionSetFlags, (Ptr)( mcFlags | mcFlagsUseCustomButton )); |
MCDoAction( inUserDataPtr->theController, mcActionSetDragEnabled, (Ptr)false ); |
MCDoAction( inUserDataPtr->theController, mcActionSetUseBadge, (Ptr)false ); |
rc = MCGetControllerBoundsRect( inUserDataPtr->theController, &theMovieBox ); |
if ( rc ) goto bail; |
inUserDataPtr->theMCHeight = theMovieBox.bottom - inUserDataPtr->theMCHeight; |
// Resize the window which now contains the attached controller |
SizeWindow( inUserDataPtr->theWindow, theMovieBox.right, theMovieBox.bottom, true ); |
AlignWindow( inUserDataPtr->theWindow, false, NULL, NULL ); |
rc = InstallStandardEventHandler( GetWindowEventTarget(inUserDataPtr->theWindow) ); |
if ( rc ) goto bail; |
rc = InstallWindowEventHandler( inUserDataPtr->theWindow, NewEventHandlerUPP( myWindowEventHandler ), GetEventTypeCount( theEventTypes ), theEventTypes, inUserDataPtr, NULL); |
if ( rc ) goto bail; |
rc = MCSetActionFilterWithRefCon( inUserDataPtr->theController, NewMCActionFilterWithRefConUPP( myMCActionFilterWithRefConProc ), (long)inUserDataPtr ); |
if ( rc ) goto bail; |
rc = InstallEventLoopTimer( GetMainEventLoop(), kEventDurationNoWait, kMCIdleDuration, NewEventLoopTimerUPP( myMovieControllerIdleTimer ), inUserDataPtr, &inUserDataPtr->theTimerRef ); |
bail: |
return rc; |
} |
/* StartVideoOutput |
*/ |
OSErr StartVideoOutput( WindowDataRecordPtr inUserDataPtr ) |
{ |
OSErr err = noErr; |
// Begin the video output, both sound and clock options are set to true by default in the interface, |
// but we need to call SetEchoPort() after Begin() to tell the VOut where to draw |
err = inUserDataPtr->pVideoOutput->Begin(); |
if ( err ) { DoError( "\pAttempting to get exclusive access to hardware failed..." ); goto bail; } |
// If the component supports an echo port, the echo port is our main window and we can resize, |
// if not, turn off controller resizing and shrink the window to our default non-echo controller size |
if ( inUserDataPtr->pVideoOutput->CanDoEchoPort() ) { |
inUserDataPtr->pVideoOutput->SetEchoPort( GetWindowPort( inUserDataPtr->theWindow ) ); |
MCMovieChanged( inUserDataPtr->theController, inUserDataPtr->theMovie ); |
SetMCResizeBounds( inUserDataPtr, true ); |
} else { |
MCSetControllerAttached( inUserDataPtr->theController, false ); |
MCSetControllerPort( inUserDataPtr->theController, GetWindowPort( inUserDataPtr->theWindow ) ); |
SetMCResizeBounds( inUserDataPtr, false ); |
SetMCEchoOffWindowSize( inUserDataPtr ); |
inUserDataPtr->pVideoOutput->SetEchoPort(); |
} |
if ( !IsWindowVisible( inUserDataPtr->theWindow ) ) { |
// Show the Window |
TransitionWindow( inUserDataPtr->theWindow, kWindowZoomTransitionEffect, kWindowShowTransitionAction, NULL ); |
MCDraw( inUserDataPtr->theController, inUserDataPtr->theWindow ); |
} |
// We're up and running |
// Set the default state of our UI |
SetMCPopupMenuState( inUserDataPtr, 0 ); |
if ( IsHighQualityOn( inUserDataPtr->theMovie ) ) { |
// High Quality is on for at least one visual track in the movie, so we |
// go ahead and turn it on for the entire movie |
SetMoviePlayHints( inUserDataPtr->theMovie, hintsHighQuality, hintsHighQuality ); |
SetMCPopupMenuState( inUserDataPtr, kHighQOnID ); |
} |
bail: |
return err; |
} |
/* DoOpen |
Life begins with a call to DoOpen(), the event handlers |
take it from there. |
*/ |
OSErr DoOpen( ConstFSSpecPtr inFSSpecPtr, WindowDataRecordPtr inUserDataPtr ) |
{ |
OSType theOpenTypeList[] = { kQTFileTypeMovie, kQTFileTypeDVC }; |
FSSpec theFSSpec; |
OSErr err = noErr; |
// Select an output component to use |
err = inUserDataPtr->pVideoOutput->SelectVideoOutputComponent(); |
if ( err ) { DoError( "\pUnable to sucessfully select a video output component..." ); goto bail; } |
// Open the video output component |
err = inUserDataPtr->pVideoOutput->Open(); |
if ( err ) { DoError( "\pUnable to sucessfully open the video output component..." ); goto bail; } |
if (inFSSpecPtr == NULL) { |
// Get the File if we don't have one from the oDoc AE |
err = GetOneFileWithPreview( 2, theOpenTypeList, &theFSSpec, NULL ); |
if ( err == userCanceledErr ) { err = noErr; goto bail; } |
if ( err ) { DoError( "\pProblem selecting file..." ); goto bail; } |
} else { |
theFSSpec = *inFSSpecPtr; |
} |
// Open the movie, create the window |
err = DoOpenMovieFromFile( &theFSSpec, inUserDataPtr ); |
if ( err ) { DoError( "\pProblem opening movie from file..." ); goto bail; } |
// Set up the Controller and install the Window Event Handlers |
err = DoCreateMovieController( inUserDataPtr ); |
if ( err ) { DoError( "\pProblem creating movie controller." ); goto bail; } |
// Tell the output component to begin |
err = StartVideoOutput( inUserDataPtr ); |
bail: |
// There's been some nastiness so reset everything |
if ( err != noErr ) { |
if ( inUserDataPtr->theTimerRef ) { |
RemoveEventLoopTimer( inUserDataPtr->theTimerRef ); |
inUserDataPtr->theTimerRef = NULL; |
} |
inUserDataPtr->pVideoOutput->End(); |
if ( inUserDataPtr->theController ) { |
DisposeMovieController( inUserDataPtr->theController ); |
inUserDataPtr->theController = NULL; |
} |
if ( inUserDataPtr->theMovie ) { |
DisposeMovie( inUserDataPtr->theMovie ); |
inUserDataPtr->theMovie = NULL; |
} |
if ( inUserDataPtr->theWindow ) { |
ReleaseWindow( inUserDataPtr->theWindow ); |
inUserDataPtr->theWindow = NULL; |
} |
inUserDataPtr->theMCHeight = 0; |
} |
return err; |
} |
int main( void ) |
{ |
Initialize(); |
RunApplicationEventLoop(); |
if (gGlobals.pVideoOutput) delete gGlobals.pVideoOutput; |
ExitMovies(); |
return 0; |
} |
Copyright © 2010 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2010-11-08