/***************************************************************************** PlayAsciiMovie -- Display a QT Movie as ASCII on a Terminal Window Environment: OS X version 10.1.5 Creation: open Project Builder click file menu -- New Project click "Standard Tool" at the end of the list name=PlayAsciiMovie, directory -- pick one ^click on PlayAsciiMovie -- new Group give it the name "Frameworks" ^click on Frameworks -- choose add frameworks choose QuickTime.framework -- reference style = default ^click on main.c under the "Source" group choose "Show Info" change Reference Style from "Group Relative" to "Project Relative" History: 05/21/02 TD Created this file and offered it through Apple's Quicktime samples. Tips: TD offers the following tips for better ASCII Quicktime Movie Viewing 10) Grow your terminal 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 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 1) Usage [smelltheglove:/Volumes/Spock] moof% ASCIIMoviePlayer sillymovie.mov *****************************************************************************/ /* Include the Quicktime header. ============================= */ #include #include /* Define source macros. ===================== */ #define ESC 27 /* Array of ASCII characters that map the luminance of a pixel to a character with the same relative luminance (i.e. density). Each position has a character in it. The first 30 positions have the space char, the next 40 positions have the '.' char, etc. ===================================================================================== */ /* Y ASCII */ /* ---------------------- */ /* 0 - 10 space */ /* 11 - 20 space */ /* 21 - 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 $ */ /* ---------------------- */ char convert[256]; /* Code the function prototypes. ============================= */ static pascal OSErr DrawCompleteProc (Movie theMovie, long refCon); /*===========================================================================* Mainline *===========================================================================*/ /* M A I N * */ int main (int argc, char *argv[]) { int i; Rect bounds; OSErr result = 0; short resRefNum = -1; short actualResId = DoTheRightThing; FSSpec theFSSpec; GWorldPtr offWorld; Movie theMovie = nil; MovieController thePlayer = nil; MovieDrawingCompleteUPP myDrawCompleteProc; // = NewMovieDrawingCompleteUPP( DrawCompleteProc); /* Make the drawing procedure known to Quicktime. ============================================== */ myDrawCompleteProc = NewMovieDrawingCompleteUPP (DrawCompleteProc); /* Load the luminance to character translation table (cf. notes on convert[] above). Each cycle of this loop loads one character. Because of the truncating divisor, each character gets loaded into approximately 10 slots of the table. =============================================================== */ for (i = 0; i < 256; ++i) { char *table = " .,:!-+=;iot76x0s&8%#@$"; convert[i] = table[i * strlen (table) / 256]; } EnterMovies (); // Initialize Quicktime /* Move the cursor to home position and erase to the end of display. ================================================================= */ printf ("%c[0;0H", ESC); printf ("%c[0J", ESC); /* Locate and open the movie file that was passed in commandline arguments. ============================================================= */ 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); /* Create a new off screen graphics world that is the same size as the movie and set it as the GWorld to use. =============================================================== */ GetMovieBox (theMovie, &bounds); QTNewGWorld (&offWorld, k32ARGBPixelFormat, &bounds, NULL, NULL, 0); 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 (1); bail: DisposeMovieController (thePlayer); DisposeMovie (theMovie); DisposeMovieDrawingCompleteUPP (myDrawCompleteProc); ExitMovies (); // Finalize Quicktime return (result); } /*===========================================================================* Functions below in Alphabetical Order *===========================================================================*/ /* D R A W C O M P L E T E P R O C * * After the frame has been drawn, Quicktime calls this to do the 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; /* Grab the needed information from the GWorld. ============================================ */ GetPixBounds (GetGWorldPixMap (offWorld), &bounds); baseAddr = GetPixBaseAddr (GetGWorldPixMap (offWorld)); rowBytes = GetPixRowBytes (GetGWorldPixMap (offWorld)); /* Send the curses escape sequence to clear the screen and move the cursor to the upper left. ============================================================================== */ printf ("%c[0;0H", ESC); /* Each cycle of this loop writes one row to the terminal window. ============================================================== */ for (y = 0; y < HEIGHT; ++y) { long *p; /* Each cycle of this loop translates and writes one 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 so luminance (Y) can be translated: (5 * R + 9 * G + 2 * B). =========================================================================== */ Y = (R + (R << 2) + G + (G << 3) + (B + B)) >> 4; /* Draw the character that corresponds to the luminance of the pixel. ================================================================== */ putchar (convert[Y]); } /* Terminate the line. =================== */ putchar ('\n'); } return noErr; } /*============================( End of Source )============================*/