ASCIIMoviePlayerSample.c

/*
    File:       ASCIIMoviePlayerSample.c
    
    Description: ASCII art QuickTime Movie Player
    
    Author:     QuickTime Engineering, DTS
 
    Copyright:  © Copyright 2002-2009 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):
        <5>     04/21/09    DTS             dont try and get a path to the file if none was supplied
        <4>     07/19/05    DTS             added universal binary compatibility for macintosh
        <3>     04/01/05    DTS             dos console goodness (crossplatform file)
        <2>     03/31/05    DTS             use data reference APIs
        <1>     05/21/02    QTEngineering   first file
*/
 
/* Top X Tips for better ASCII QuickTime Movie Viewing
    11) Remember Tom Dowdy, he originally wrote this and is missed by all of us
    10) Grow your terminal/console to fit the Movie
    9) Ask marketing folks if you can incorporate this code into your latest QuickTime product
       and see if they think you're serious, then do it behind their back anyway
    8) Set your terminal to White on Black for optimal look
    7) Download your favorite movie trailer
    6) While you're at it, download some Graphics Importer sample code (why not?)
    5) Jedi mind trick your manager "...you want to send me to WWDC"
    4) Order the pizza.
    3) Dim the lights and turn up the audio
    2) Turn off terminal transparancy for fastest performance
    1a) Usage [smelltheglove:/Volumes/Spock] moof% ASCIIMoviePlayer sillymovie.mov
    1b) Usage C:\ ASCIIMoviePlayer.exe sillymovie.mov
*/
 
#if TARGET_OS_WIN32
    #include "stdafx.h"
    #define pascal
    #define USE_QD_ACCESSORS 1
#else
    #include <stdio.h>
    #include <QuickTime/QuickTime.h>
    #define ESC 27
#endif
 
#if TARGET_OS_WIN32
HANDLE hStdOut = NULL;
 
#if USE_QD_ACCESSORS
Rect * GetPixBounds(PixMapHandle pixMap, Rect *bounds)
{
    *bounds = (**pixMap).bounds;
 
    return bounds;
}
#endif
 
void GoHome(void)
{
    COORD coord = {0, 0};
 
    SetConsoleCursorPosition(hStdOut, coord);
}
 
void ClearScreen(void)
{
    COORD coord = {0, 0};
    DWORD count;
    CONSOLE_SCREEN_BUFFER_INFO csbi;
 
    GetConsoleScreenBufferInfo(hStdOut, &csbi);
    FillConsoleOutputCharacter(hStdOut, ' ', csbi.dwSize.X * csbi.dwSize.Y, coord, &count);
    GoHome();
}
#endif
 
// ASCII pixel value array
char convert[256];
 
// DrawCompleteProc - After the frame has been drawn QuickTime calls us to do some work
static pascal OSErr DrawCompleteProc(Movie theMovie, long refCon)
{
#define SCALE (2.25)
#if 0
    // 16x9
    #define WIDTH   ((float)(80*SCALE))
    #define HEIGHT  ((float)(17*SCALE))
#else
    // 4x3
    #define WIDTH   ((float)(80*SCALE))
    #define HEIGHT  ((float)(24*SCALE))
#endif
 
    int y, x;
    GWorldPtr   offWorld = (GWorldPtr)refCon;
    Rect        bounds;
    Ptr         baseAddr;
    long        rowBytes;
    
    // get the information we need from the GWorld
    GetPixBounds(GetGWorldPixMap(offWorld), &bounds);
    baseAddr = GetPixBaseAddr(GetGWorldPixMap(offWorld));
    rowBytes = QTGetPixMapHandleRowBytes(GetGWorldPixMap(offWorld));
 
#if TARGET_OS_WIN32
    GoHome();
#else
    // goto home
    printf("%c[0;0H", ESC);
#endif
    
    // for each row
    for (y = 0; y < HEIGHT; ++y) {
        long    *p;
        
        // for each pixel
        p = (long*)(baseAddr + rowBytes * (long)(y * ((bounds.bottom - bounds.top) / HEIGHT)));
        for (x = 0; x < WIDTH; ++x) {
            UInt32          color;
            long            Y;
            long            R;
            long            G;
            long            B;
 
            color = *(long *)((long)p + 4 * (long)(x*(bounds.right - bounds.left) / WIDTH));
            R = (color & 0x00FF0000) >> 16;
            G = (color & 0x0000FF00) >> 8;
            B = (color & 0x000000FF) >> 0;
 
            // convert to YUV for comparison
            // 5 * R + 9 * G + 2 * B
            Y = (R + (R << 2) + G + (G << 3) + (B + B)) >> 4;
            
            // draw it
            putchar(convert[Y]);
        }
        
        // next line
        putchar('\n');
    }
    
    return noErr;
}
 
#if TARGET_OS_WIN32
int _tmain(int argc, _TCHAR* argv[])
#else
int main(int argc, char *argv[])
#endif
{
    MovieController thePlayer = NULL;
    Movie       theMovie = NULL;
    GWorldPtr   offWorld;
    Rect        bounds;
    short       actualResId = DoTheRightThing;
    int i;
    OSErr       result = 0;
    MovieDrawingCompleteUPP myDrawCompleteProc = NewMovieDrawingCompleteUPP(DrawCompleteProc);
    
#if TARGET_OS_WIN32
    FSSpec theFSSpec;
    short  resRefNum = -1;
#else
    // Using Data Reference calls now
    OSType      myDataRefType;
    Handle      myDataRef = NULL;
    CFStringRef inPath;
#endif
    
    /* build the luminance value to ASCII value conversion table
           Y              ASCII
        0 - 30            space
        31 - 40             .
        41 - 51             ,
        52 - 61             :
        62 - 71             !
        72 - 81             -
        82 - 92             +
        93 - 102            =
        103 - 112           ;
        113 - 122           i
        123 - 133           o
        134 - 143           t
        144 - 153           7
        154 - 163           6
        164 - 174           x
        175 - 184           0
        185 - 194           s
        195 - 204           &
        205 - 215           8
        216 - 225           %
        226 - 235           #
        236 - 245           @
        246 - 255           $
    */
    for (i = 0; i < 256; ++i) {
        char *table = "   .,:!-+=;iot76x0s&8%#@$";
        convert[i] = table[i * strlen(table) / 256];
    }
        
#if TARGET_OS_WIN32
    InitializeQTML(0);
    EnterMovies();
 
    hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
    if (NULL == hStdOut) return -1;
    
    ClearScreen();
 
    result = NativePathNameToFSSpec(argv[1], &theFSSpec, 0 /* flags */);
    if (result) {printf("NativePathNameToFSSpec failed %d\n", result); goto bail; }
    
    result = OpenMovieFile(&theFSSpec, &resRefNum, 0);
    if (result) {printf("OpenMovieFile failed %d\n", result); goto bail; }
        
    result = NewMovieFromFile(&theMovie, resRefNum, &actualResId, (unsigned char *) 0, 0, (Boolean *) 0);
    if (result) {printf("NewMovieFromFile failed %d\n", result); goto bail; }
 
    if (resRefNum != -1)
        CloseMovieFile(resRefNum);
#else
    EnterMovies();
 
    // home
    printf("%c[0;0H", ESC);
    // erase to end of display
    printf("%c[0J", ESC);
 
    // Convert movie path to CFString
    if (argc > 1) {
        inPath = CFStringCreateWithCString(NULL, argv[1], CFStringGetSystemEncoding());
        if (!inPath) { printf("Could not get CFString\n"); goto bail; }
    } else { printf("You need to at least type a path to a .mov file!\n"); goto bail; }
    
    // create the data reference
    result = QTNewDataReferenceFromFullPathCFString(inPath, kQTNativeDefaultPathStyle,
                                                    0, &myDataRef, &myDataRefType);
    if (result) { printf("Could not get DataRef %d\n", result); goto bail; }
 
    // get the Movie
    result = NewMovieFromDataRef(&theMovie, newMovieActive,
                                 &actualResId, myDataRef, myDataRefType);
    if (result) { printf("Could not get Movie from DataRef %d\n", result); goto bail; }
 
    // dispose the data reference handle - we no longer need it
    DisposeHandle(myDataRef);
#endif
 
    GetMovieBox(theMovie, &bounds);
    
    // use kNativeEndianPixMap flag so we do the right thing on Intel-based macs
    QTNewGWorld(&offWorld, k32ARGBPixelFormat, &bounds, NULL, NULL, kNativeEndianPixMap);
    LockPixels(GetGWorldPixMap(offWorld));
    SetGWorld(offWorld, NULL);
    
    thePlayer = NewMovieController(theMovie, &bounds, mcTopLeftMovie | mcNotVisible);
    SetMovieGWorld(theMovie, offWorld, NULL);
    SetMovieActive(theMovie, true);
    SetMovieDrawingCompleteProc(theMovie, movieDrawingCallWhenChanged, myDrawCompleteProc, (long)offWorld); 
    MCDoAction(thePlayer, mcActionPrerollAndPlay, (void *)Long2Fix(1));
    
    do {
        MCIdle(thePlayer);
    } while (!IsMovieDone(theMovie));
    
bail:
    if (thePlayer) DisposeMovieController(thePlayer);
    if (theMovie) DisposeMovie(theMovie);
    if (myDrawCompleteProc) DisposeMovieDrawingCompleteUPP(myDrawCompleteProc);
        
    return result;
}