VideoFrameToGWorld.c

/*
    File:       VideoFrameToGWorld.c
    
    Description: VideoFrameToGWorld is an example of decompressing frames from a video track
                 into an offscreen buffer so the pixels can be manipulated at a later time.
 
    Author:     era based on code provided by Kevin Marks
 
    Copyright:  © Copyright 2000 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): <1> 4/1/00 initial release
 
*/
 
#include "VideoFrameToGWorld.h"
 
// Globals
//------------------------------------------------------------------------------
    WindowRef   gWindow = NULL;
    GWorldPtr   gSrcGWorld = NULL;
    Movie       gMovie = NULL;
    TimeValue   gMovieTime = 0;         // set current time value to begining of the Movie
    UInt32      gFrameCount = -1;
    UInt32      gFrameNumber = 0;
    Rect        gMovieBox;
 
//------------------------------------------------------------------------------
//  Open up the Movie file and get a Movie from it.
//------------------------------------------------------------------------------
void OpenMovie( void )
{
    FSSpec  theFSSpec;
    short   refnum = 0;
    OSType  openTypeList[] = {kQTFileTypeMovie};
    short   numTypes = 1;
    
    OSErr err = noErr;
 
    err = GetOneFileWithPreview(numTypes, openTypeList, &theFSSpec, NULL);
    BailError(err);
 
    // open a Movie file using the FSSpec and create a Movie from that file.    
    err = OpenMovieFile(&theFSSpec, &refnum, fsRdPerm);
    BailError(err);
 
    NewMovieFromFile(&gMovie, refnum, NULL, NULL, newMovieActive, NULL);    
 
bail:   
    //  we're done with the file.
    if (refnum)
        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.
//------------------------------------------------------------------------------
void CountThemFrames( void )
{
    OSType      whichMediaType = VIDEO_TYPE;
    short       flags = nextTimeMediaSample + nextTimeEdgeOK;
    TimeValue   duration;
    TimeValue   theTime = 0;
    
    while (theTime >= 0) {
        gFrameCount++;
        GetMovieNextInterestingTime(gMovie,
                                    flags,
                                    1,
                                    &whichMediaType,
                                    theTime,
                                    0,
                                    &theTime,
                                    &duration);
 
        //  after the first interesting time, don't include the time we
        //  are currently at.
 
        flags = nextTimeMediaSample;
    } // while
}
 
//------------------------------------------------------------------------------
//  Get the bounding rectangle of the Movie the create a 32-bit GWorld
//  with those dimensions.
//  This GWorld will be used for rendering Movie frames into.
//------------------------------------------------------------------------------
void MakeGWorld( void )
{
    Rect srcRect;
    Rect portRect;
    RGBColor theBlackColor = { 0, 0, 0 };
    RGBColor theWhiteColor = { 65535, 65535, 65535 };
    
    OSErr err = noErr;
    
    GetMovieBox(gMovie,&srcRect);
    
    err = NewGWorld(&gSrcGWorld,
                    k32ARGBPixelFormat,
                    &srcRect,
                    NULL,
                    NULL,
                    0);
    BailError(err);
    
    LockPixels(GetGWorldPixMap(gSrcGWorld));
 
    SetGWorld(gSrcGWorld,NULL);
    GetPortBounds(gSrcGWorld, &portRect);
    RGBBackColor(&theBlackColor);
    RGBForeColor(&theWhiteColor);
    EraseRect(&portRect);       
 
bail:
    return;
}
 
//------------------------------------------------------------------------------
//  Blit the contents of the GWorld to the window
//------------------------------------------------------------------------------
void BlitToScreen( void )
{
    Rect srcRect;
    Rect destRect;
    
    GetPortBounds(gSrcGWorld, &srcRect);
    GetPortBounds(GetWindowPort(gWindow), &destRect);
    CopyBits(GetPortBitMapForCopyBits(gSrcGWorld),
             GetPortBitMapForCopyBits(GetWindowPort(gWindow)),
             &srcRect,
             &destRect,
             srcCopy,
             NULL);
}
 
//------------------------------------------------------------------------------
//  First get the next frame of the movie, set the movie time for that frame,
//  then task the movie which will draw the frame to the GWorld
//  Modify the movie matrix so the next frame will be rotated just for fun and
//  finally draw the frame number on top of the image and inval the window rect
//------------------------------------------------------------------------------
void NextFrame( void )
{
    CGrafPtr        savedPort;
    GDHandle        savedDevice;
    Rect            theRect;
    char            frame[32];
    Str255          theString;
    Rect            invalRect;
    MatrixRecord    theMovieMatrix;
    
    // these don't change, always rotate 30 degrees anchored in
    // the center of the frame
    static Fixed degrees = Long2Fix(30);
    static Fixed x = Long2Fix((gMovieBox.right - gMovieBox.left) / 2);
    static Fixed y = Long2Fix((gMovieBox.bottom - gMovieBox.top) / 2);
    
    if ( gFrameNumber < gFrameCount ) {
 
        TimeValue duration;
        
        GetGWorld(&savedPort, &savedDevice);
        SetGWorld(gSrcGWorld, NULL);
        
        // get the next frame of the source movie
        short   flags = nextTimeMediaSample;
        OSType  whichMediaType = VIDEO_TYPE;
 
        // if this is the first frame, include the frame we are currently on
        if (gFrameNumber == 0)
            flags |= nextTimeEdgeOK;
 
        // skip to the next interesting time and get the duration for that frame
        GetMovieNextInterestingTime(gMovie,
                                    flags,
                                    1,
                                    &whichMediaType,
                                    gMovieTime,
                                    0,
                                    &gMovieTime,
                                    &duration);
 
        // set the time for the frame and give time to the movie toolbox
        SetMovieTimeValue(gMovie,gMovieTime);
        GetPortBounds(gSrcGWorld, &theRect);
        EraseRect(&theRect);    // paint the background black
        
        // *** this does the actual drawing into the GWorld ***
        MoviesTask(gMovie,0);
 
//------------------------------------------------------------------------------
// You now have pixels you can play with in the GWorld!
// This sample simply blits the contents of the GWorld back to
// a window with the frame number drawn on top of the image
//------------------------------------------------------------------------------
        
        // have some fun and rotate the movie frame
        GetMovieMatrix(gMovie, &theMovieMatrix);
        RotateMatrix(&theMovieMatrix, degrees, x, y);
        SetMovieMatrix(gMovie, &theMovieMatrix);        
 
        gFrameNumber++;
 
        // draw the frame number on top of the image        
        TextSize(12);
        TextMode(srcCopy);
        MoveTo( theRect.left + 5, theRect.bottom - 15 );
        sprintf(frame,"Frame #%ld",gFrameNumber);
        CopyCStringToPascal(frame, theString);
        DrawString(theString);
        
        SetGWorld(savedPort, savedDevice);
        
        // inval the window to generate an update event
        GetPort(&savedPort);
        SetPortWindowPort(gWindow);
        
        GetWindowBounds(gWindow, kWindowContentRgn, &invalRect);
        GlobalToLocal( (Point *)&invalRect.top);
        InvalWindowRect(gWindow, &invalRect);
        
        SetPort(savedPort);
        
    } else {
        // reset the movie time back to the beginning
        // then do it all over again
        gMovieTime = 0;
        gFrameNumber = 0;
        NextFrame();
    }
}
 
int main()
{
    Rect rectWnd;
    Rect rectMovie;
    WindowRef pWhichWindow;
    short nPart;
    EventRecord theEvent;
    Boolean bDone = false;
    
    OSErr result = noErr;
    
    // initialize for Carbon and QuickTime      
    InitCursor();
    result = EnterMovies();
    BailError(result);
 
    // create the window
    SetRect(&rectWnd, 100, 100, 200, 200);
    gWindow = NewCWindow(NULL, &rectWnd, "\pVideoFrameToGworld", false, kWindowDocumentProc, (WindowPtr)-1, true, NULL);
    BailNULL(gWindow);
    SetPortWindowPort(gWindow);
    
    // get the movie
    OpenMovie();
    BailNULL(gMovie);
    
    // normalize the movie rect
    GetMovieBox(gMovie, &rectMovie);
    OffsetRect(&rectMovie, -rectMovie.left, -rectMovie.top);
    SetMovieBox(gMovie, &rectMovie);
    gMovieBox = rectMovie;
    
    SizeWindow(gWindow, rectMovie.right, rectMovie.bottom, true);
    ShowWindow(gWindow);
    
    CountThemFrames();
    
    // create the offscreen to draw into
    MakeGWorld();
    BailNULL(gSrcGWorld);
    
    // *** set the graphics world for displaying the movie ***
    SetMovieGWorld(gMovie, gSrcGWorld, GetGWorldDevice(gSrcGWorld));
    
    // get the first frame
    NextFrame();
        
    while (!bDone) {
        if (WaitNextEvent(everyEvent, &theEvent, 0, NULL)) {
            switch (theEvent.what) {
            case updateEvt:
                pWhichWindow = (WindowPtr)theEvent.message;
                if (pWhichWindow == gWindow) {
                    // draw the frame
                    GrafPtr savedPort;
                    CGrafPtr windowPort = GetWindowPort(pWhichWindow);
                    RgnHandle vizRgn = NewRgn();
                    RgnHandle savedClip = NewRgn();
 
                    GetPort(&savedPort);
                    SetPort(windowPort);
                    GetClip(savedClip);
                    
                    BeginUpdate(pWhichWindow);
                        GetPortVisibleRegion(windowPort, vizRgn);
                        SetClip(vizRgn);
                        BlitToScreen();
                    EndUpdate(pWhichWindow);
                    
                    SetClip(savedClip);
                    SetPort(savedPort);
                    DisposeRgn(savedClip);
                    DisposeRgn(vizRgn);
                }
                break;
 
            case mouseDown:
                nPart = FindWindow(theEvent.where, &pWhichWindow);
                if (pWhichWindow == gWindow) {
                    switch (nPart) {
                    case inGoAway:
                        // we're done
                        bDone = TrackGoAway(pWhichWindow, theEvent.where);
                        break;
                        
                    case inContent:
                        // get the next frame
                        NextFrame();
                        break;
                                            
                    case inDrag:
                        BitMap screenBits;
                        GetQDGlobalsScreenBits(&screenBits);
                        DragWindow(pWhichWindow, theEvent.where, &screenBits.bounds);
                        break;
                    } // switch
                }
                break;
            } // switch
        }
    } // while
    
bail:
    if ( gMovie )
        DisposeMovie(gMovie);
        
    if ( gWindow )
        DisposeWindow(gWindow);
}