home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Applications / RTrace 1.0 / source / macmovie.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-10-19  |  16.0 KB  |  600 lines  |  [TEXT/KAHL]

  1. /*****************************************************************************\
  2. * Module: macmovie.c                                                          *
  3. *                                                                             *
  4. * Purpose:                                                                       *
  5. *    This is the source for a simple utility to convert PICT files             *
  6. *    into QuickTime movies.  It was based upon the source in the QuickTime     *
  7. *   Developers (CD Version 1.0) sample-C-code directory: PICStoMovie.          *
  8. *                                                                              *
  9. *   It shows how to use the Standard Compression library to request           *
  10. *   compression setting from the user, how to use the Image Compression          *
  11. *    Manager to compress sequences of images, and how to use the Movie           *
  12. *   Toolbox to create simple QuickTime movies.                                  *
  13. * Usage:                                                                      *
  14. *    You will need to produce a sequence of PICT files of the format:          *
  15. *    "pictfilename.#" where "#" begins with 1 and continues until the movie    *
  16. *   ends, we reach the end of long integers = 2^32-1, or you run out of       *
  17. *   disk space.                                                                  *
  18. *   This program will ask you for the location of the first frame in the      *
  19. *   sequence (pictfilename.1) and will also give you the option of saving     *
  20. *   your movie in any of the currently installed codecs (compressions) that   *
  21. *   are available.                                                              *
  22. *                                                                             *
  23. * Created by: Reid Judd (ILLUMINATI@AppleLink.apple.com)                      *
  24. * Created on: September 23, 1992                                              *
  25. * Modified:                                                                   *
  26. *    reid        Sept 29, 92        Call abortive_error() if (result != 0) after  *
  27. *                                  cleanup().  Also restore inReply->fName     *
  28. \*****************************************************************************/
  29.  
  30.  
  31. #include <stdio.h>
  32.  
  33. #include <memory.h>
  34. #include <resources.h>
  35. #include <menus.h>
  36. #include <osevents.h>
  37. #include <events.h>
  38. #include <fonts.h>
  39. #include <errors.h>
  40.  
  41. #include "movies.h"
  42. #include "moviesformat.h"
  43. #include "macerrors.h"
  44.  
  45. #include "StdCompression.h"
  46.  
  47.  
  48. /*    These macros for error checking all force us to go thru cleanup() to
  49.  *  try to sweep up any messes we've made before we call the abortive_error()
  50.  *  routine in macerrors.c.  This routine will display a message and then
  51.  *  attempt to put us back in the main loop again. 
  52.  */
  53.  
  54. #define    BailOnError(result)    if (result) cleanup();
  55. #define    BailOnNil(p)        if (!p) { result = -1; cleanup();}
  56.  
  57.  
  58. /* Globals, needed for cleanup() so it can deallocate resources.
  59.  */
  60.  
  61. short                dstMovieRefNum = 0;
  62. Movie                dstMovie = nil;
  63.  
  64. ImageSequence        srcSeqID = 0;
  65. ImageSequence        dstSeqID = 0;
  66.     
  67. short                srcResRefNum = 0;
  68. Handle                compressedData = nil;
  69. ImageDescription    **idh = nil;
  70. WindowPtr            progressWindow = nil;
  71. GWorldPtr            pictGWorld = nil;
  72.  
  73. OSErr                result = noErr;
  74. short                quitFlag = FALSE;
  75.  
  76. ComponentInstance    ci;        /* Initialized in macstuff.c, init_mac.  Must be inited
  77.                                 *   before calling EnterMovies() to init QuickTime.
  78.                                 */
  79.  
  80.  
  81. /* ---------------------------------------------------------------
  82.  * loadPict()
  83.  */
  84. PicHandle LoadPict( SFReply *Rep )
  85. {
  86.     GDHandle saveDevice;
  87.     CGrafPtr savePort;
  88.      Str255 volName;
  89.     short curVolRefNum, refNum;
  90.     char buf[512];
  91.     long len;
  92.     PicHandle qdpic;
  93.     GWorldPtr mygw;
  94.  
  95.     result = FSOpen( Rep->fName, Rep->vRefNum, &refNum );
  96.     /* Error for file not found might mean that we're at the end of the sequence. */
  97.     if (result == fnfErr)
  98.       return NULL;  
  99.       
  100.     /* Error loading PICT file, can't open file (for reason other than fnfErr). */  
  101.     BailOnError(result);
  102.     
  103.     result = GetEOF( refNum, &len );
  104.     /* Error loading PICT file, can't get EOF */
  105.     BailOnError(result);
  106.     
  107.     if (len <= 512)
  108.     {
  109.         /* Pict file is empty */
  110.         result = FSClose( refNum );
  111.         return NULL;
  112.     }
  113.     result = SetFPos( refNum, fsFromStart, 512L );
  114.     /* Error loading PICT file, can't skip header */
  115.     BailOnError(result);    
  116.     
  117.     qdpic = (PicHandle) NewHandle( len - 512L );
  118.     /* Error loading PICT file, not enough memory */
  119.     BailOnNil( qdpic );
  120.  
  121.     HLock( qdpic );
  122.     len -= 512L;
  123.     result = FSRead( refNum, &len, (Ptr) (*qdpic) );
  124.     /* Error loading PICT file, can't read data */
  125.     BailOnError(result);        
  126.     
  127.     result = FSClose( refNum );
  128.     /* Error loading PICT file, can't close file */
  129.     BailOnError(result);
  130.     
  131.     /* unlock the handle to the pict. */    
  132.     HUnlock( qdpic );    
  133.  
  134.     return( qdpic );    
  135. }
  136.  
  137. /* --------------------------------------------------------------- */
  138.  
  139. void convert_picts_to_movie( SFReply *inReply )
  140.  
  141. {
  142.      SCParams            p;
  143.     Point                where;
  144.      SFReply                outReply;
  145.     SFTypeList            typeList;
  146.     Rect                r;
  147.     short                hstate;
  148.     short                i;
  149.     
  150.     int                 framenum;
  151.     PicHandle            thePict;
  152.     Rect                pictRect;
  153.       long                compressedFrameSize;
  154.      
  155.      Media                dstMedia = nil;
  156.     Track                dstTrack = nil;
  157.  
  158.      GDHandle            progressGDevice;
  159.  
  160.     GDHandle             saveDevice;
  161.     CGrafPtr             savePort;    
  162.  
  163.     FSSpec                theFSSpec;
  164.     TimeScale            dstTimeScale;
  165.     long                frames_per_second = 12;
  166.     char                 filename[255];
  167.  
  168.     /***************************************
  169.      *
  170.      *    Get destination movie file, 
  171.      *   setup filename, get first pict in sequence.
  172.      *
  173.      ***************************************/
  174.  
  175.     /*
  176.      *    Default destination movie file name is 
  177.      *  "pictfile.qt.movie".  Save the 'filename',
  178.      *  we'll use it again to read in the rest of the frames.
  179.      */
  180.  
  181.     BlockMove(inReply->fName,outReply.fName,64);
  182.       outReply.good = outReply.copy = outReply.fType = outReply.version = 0;
  183.     outReply.vRefNum = inReply->vRefNum;
  184.     p2cstr(outReply.fName);
  185.     strcpy(filename, (char *) outReply.fName );
  186.     sprintf( (char *) outReply.fName,"%s.movie", filename );
  187.     c2pstr(outReply.fName);
  188.         
  189.      
  190.     /* Load the first pict of the sequence.  
  191.      *   It should have the name: pictfile.qt.1  
  192.      *   Default movie name will be based upon this name.
  193.      */         
  194.     framenum = 1;
  195.     p2cstr( inReply->fName );
  196.     sprintf( (char *) inReply->fName, "%s.%d", filename, framenum );
  197.     c2pstr( inReply->fName );
  198.  
  199.     thePict = LoadPict( inReply );
  200.  
  201.     BailOnNil(thePict);
  202.     pictRect = (*thePict)->picFrame;
  203.  
  204.  
  205.     /***************************************
  206.      *
  207.      *    Setup QuickTime.
  208.      *
  209.      ***************************************/ 
  210.          
  211.     /*    Open the standard compression dialog component. 
  212.      *    (this has already been done in macstuff.c, init_mac() ).
  213.      *     
  214.        ci = OpenStdCompression();
  215.        BailOnNil(ci);
  216.     
  217.        result = EnterMovies();
  218.        BailOnError(result);
  219.       ****/
  220.          
  221.     /*
  222.      *    Fill in default settings for compression dialog.  These will be the
  223.      *    settings displayed when the compression dialog is first displayed.
  224.      *    Note that these settings are only set up once so that if the dialog
  225.      *    is used more than once, the user's settings from the previous time
  226.      *    will be the defaults for the next time.
  227.      */
  228.  
  229.     p.flags = scShowMotionSettings;
  230.     p.theCodecType = 'rpza';
  231.     p.theCodec = anyCodec;
  232.     p.spatialQuality = codecNormalQuality;
  233.     p.temporalQuality = codecNormalQuality;
  234.     p.depth = 8;
  235.     p.frameRate = (long)10 << 16;
  236.     p.keyFrameRate = 100;
  237.  
  238.  
  239.     /***************************************
  240.      *
  241.      *    Ask user for compression parameters.
  242.      *
  243.      ***************************************/
  244.  
  245. ReaskOptions:
  246.     
  247.     /*
  248.      *    Save the purgeable state of the first PICT resource and then
  249.      *    make it unpurgeable.  We don't want the resource getting purged
  250.      *    while we're using it for the test image in standard compression.
  251.      */
  252.  
  253.     hstate = HGetState((Handle)thePict);
  254.     HNoPurge((Handle)thePict);
  255.     
  256.     /*    Set the PICT as the test image for standard compression. */
  257.     
  258.     SCSetTestImagePictHandle(ci,thePict,nil,0);
  259.     
  260.     /*
  261.      *    Get compression settings from user.  Center dialog on best screen
  262.      *    by setting where to (-2,-2)
  263.      */
  264.     
  265.     where.h = where.v = -2;
  266.     result = SCGetCompression(ci,&p,where);
  267.     
  268.     /*    Return picture to its previous purge state. */
  269.     
  270.     HSetState((Handle)thePict,hstate);
  271.     
  272.     /*    If the user selected Cancel, go ask for a new source PICS file. */
  273.     
  274.     if (result == 1)
  275.         cleanup();
  276.     BailOnError(result);
  277.  
  278.  
  279.     /***************************************
  280.      *
  281.      *    Open a progress window.
  282.      *
  283.      ***************************************/
  284.     
  285.     /*    Use standard compression routines to center window on best device. */
  286.     where.h = where.v = -2;
  287.  
  288.     r = pictRect;
  289.     SCPositionRect(ci,&r,&where);
  290.     progressWindow = NewCWindow(0,&r,outReply.fName,true,0,(WindowPtr)-1,false,0);
  291.     BailOnNil(progressWindow);
  292.     SetPort(progressWindow);
  293.     
  294.     /*    Set coordinate system of window to match that of picture. */
  295.     
  296.     SetOrigin(pictRect.left,pictRect.top);
  297.     progressGDevice = GetGDevice();
  298.  
  299.  
  300.     /***************************************
  301.      *
  302.      *    Prepare GWorld to draw pictures into.
  303.      *
  304.      ***************************************/
  305.  
  306.     result = NewGWorld(&pictGWorld,p.depth,&pictRect,nil,nil,0);
  307.     BailOnError(result);
  308.     LockPixels(pictGWorld->portPixMap);
  309.     
  310.     GetGWorld( &savePort, &saveDevice );
  311.  
  312.     SetGWorld(pictGWorld,nil);
  313.     EraseRect(&pictRect);
  314.     UnlockPixels(pictGWorld->portPixMap);
  315.  
  316.  
  317.     /***************************************
  318.      *
  319.      *    Set up movie file.
  320.      *
  321.      ***************************************/
  322.      
  323.     frames_per_second = (p.frameRate + 0x00008000) >> 16;
  324.     
  325.     /*    Create an FSSpec for the destination file. */
  326.         
  327.     result = FSMakeFSSpec(outReply.vRefNum,0,outReply.fName,&theFSSpec);
  328.     if (result && result != fnfErr)
  329.         cleanup();
  330.         
  331.     /*    Create the movie file, deleting the old one if it exists. */
  332.         
  333.     result = CreateMovieFile(&theFSSpec,'TVOD',0,createMovieFileDeleteCurFile,
  334.                             &dstMovieRefNum,&dstMovie);
  335.     BailOnError(result);
  336.         
  337.     /*
  338.      *    Create a new track with the picture's dimensions.
  339.      *    Note that the dimensions are fixed point numbers.
  340.      */        
  341.     dstTrack = NewMovieTrack(dstMovie,(long)(pictRect.right - pictRect.left) << 16,
  342.                                     (long)(pictRect.bottom - pictRect.top) << 16,0);
  343.         
  344.     /*
  345.      *    Create a new video media with a time scale that is large
  346.      *    enough to accurately handle a fractional frame rate.
  347.      */        
  348.     dstTimeScale = 60;
  349.     while (frames_per_second > dstTimeScale)
  350.         dstTimeScale *= 10;
  351.     dstMedia = NewTrackMedia(dstTrack,VIDEO_TYPE,dstTimeScale,0,0);
  352.     result = BeginMediaEdits(dstMedia);
  353.     BailOnError(result);
  354.  
  355.  
  356.     /***************************************
  357.      *
  358.      *    Prepare compression sequence.
  359.      *
  360.      ***************************************/
  361.  
  362.     /*    Create an uninitialized image desciption. */
  363.  
  364.     idh = (ImageDescription**)NewHandle(sizeof(ImageDescription));
  365.     
  366.     /*
  367.      *    Find out how large the largest possible compressed frame
  368.      *    will be, allocated memory for it, and lock it down.
  369.      */
  370.     
  371.     result = GetMaxCompressionSize(pictGWorld->portPixMap,&pictRect,p.depth,p.spatialQuality,
  372.                 p.theCodecType,p.theCodec,&compressedFrameSize);
  373.     BailOnError(result);
  374.     compressedData = NewHandle(compressedFrameSize);
  375.     BailOnNil(compressedData);
  376.     HLock(compressedData);
  377.     
  378.     /*
  379.      *    Begin the compression sequence.  Note the the image desciption will still not be
  380.      *    initialized after the CompressSequenceBegin call, so now is not the time to be
  381.      *    looking at its contents.  This may change in the future.
  382.      */
  383.     
  384.     result = CompressSequenceBegin(&srcSeqID,pictGWorld->portPixMap,nil,&pictRect,nil,p.depth,
  385.                 p.theCodecType,p.theCodec,p.spatialQuality,p.temporalQuality,
  386.                 p.keyFrameRate,nil,codecFlagUpdatePrevious,idh);
  387.     BailOnError(result);
  388.  
  389.  
  390.     /***************************************
  391.      *
  392.      *    Create and compress frames. Start at frame 1 and continue 
  393.      *  until a there are no more frames with the format:
  394.      *        pictfile.#
  395.      *
  396.      ***************************************/
  397.  
  398.      framenum = 1;
  399.      while (quitFlag == FALSE) {
  400.  
  401.         unsigned char    similarity;
  402.         short            syncFlag;
  403.         TimeValue        duration;
  404.         long            flags;
  405.         
  406.         /*    Let the user abort by pressing the button. */
  407.         
  408.         if (Button())
  409.             break;
  410.  
  411.         /*    Draw the next image from the PICS file into the pictGWorld. */
  412.  
  413.         SetGWorld(pictGWorld,nil);
  414.         {            
  415.              Rect    r;
  416.              
  417.              r = (*thePict)->picFrame;
  418.             DrawPicture(thePict,&r);
  419.  
  420.             ReleaseResource((Handle)thePict); 
  421.            
  422.         }
  423.         SetGWorld((CGrafPtr)progressWindow,progressGDevice);
  424.  
  425.         /*
  426.          *    Compress a frame.  The flags are set to codecFlagUpdatePrevious + PreviousComp
  427.          *    so that frame differencing is cleanup with the previous compressed image instead
  428.          *    of the previous source image.  This gives more accurate frame differencing.
  429.          *
  430.          *    Note that *compressedData is StripAddress-ed.  Data pointers passed into
  431.          *    image compression manager routines must be 32-bit clean.
  432.          */
  433.  
  434.         flags = codecFlagUpdatePrevious + codecFlagUpdatePreviousComp;
  435.         result = CompressSequenceFrame(srcSeqID,pictGWorld->portPixMap,&pictRect,flags,
  436.                     StripAddress(*compressedData),&compressedFrameSize,&similarity,nil);
  437.         BailOnError(result);
  438.  
  439.         /*
  440.          *    If this is the first frame, begin the decompression sequence for displaying
  441.          *    in the progress window.  We have to wait until after the the first
  442.          *    CompressSequenceFrame before we can use the image description handle idh because
  443.          *    it doesn't get initialized until after a CompressSequenceFrame.
  444.          */
  445.  
  446.         if (framenum == 1) {
  447.             result = DecompressSequenceBegin(&dstSeqID,idh,nil,nil,&pictRect,nil,ditherCopy,
  448.                         nil,0,codecNormalQuality,anyCodec);
  449.             BailOnError(result);
  450.         }
  451.  
  452.         /*
  453.          *    Add the newly compressed frame to the dstMedia.  We set the syncFlag based on
  454.          *    the similarity returned by CompressSequenceFrame.  If the similarity is non-zero,
  455.          *    the frame has some frame differencing in it and therefore is not a sync sample.
  456.          *    If the similarity is zero, there is no frame differencing and the frame is a
  457.          *    sync/key frame.
  458.          */
  459.          
  460.         /* default duration is set to 12 frames/sec: 60/12 = 5 */
  461.         duration = dstTimeScale/frames_per_second;  
  462.  
  463.         syncFlag = (similarity ? mediaSampleNotSync : 0);
  464.         result = AddMediaSample(dstMedia,compressedData,0,compressedFrameSize,
  465.                     duration,
  466.                     (SampleDescriptionHandle)idh,1,syncFlag,nil);
  467.         BailOnError(result);
  468.         
  469.         /*
  470.          *    Decompress the current frame to the progress window.  Again notice that
  471.          *    the dereferenced compressedData handle was StripAddressed.
  472.          */
  473.         
  474.         if (progressWindow) {
  475.             CodecFlags    outFlags;
  476.             result = DecompressSequenceFrame(dstSeqID,StripAddress(*compressedData),0,&outFlags,nil);
  477.             BailOnError(result);
  478.         }
  479.         
  480.         
  481.         /* Get the next frame in the sequence.   
  482.          */
  483.         framenum++;
  484.     
  485.         p2cstr( inReply->fName );
  486.          sprintf( (char *) inReply->fName, "%s.%d", filename, framenum );
  487.         c2pstr( inReply->fName );
  488.  
  489.         thePict = LoadPict( inReply );
  490.          if (thePict == NULL)
  491.           quitFlag = TRUE;                 
  492.     }
  493.     HUnlock((Handle)compressedData);
  494.  
  495.  
  496.     /***************************************
  497.      *
  498.      *    Finish creating destination movie file.
  499.      *
  500.      ***************************************/
  501.  
  502.     {
  503.         short resID = 0;
  504.         result = EndMediaEdits(dstMedia);
  505.         BailOnError(result);
  506.         InsertMediaIntoTrack(dstTrack,0,0,GetMediaDuration(dstMedia),1L<<16);
  507.         BailOnError(result = GetMoviesError());
  508.      
  509.         result = AddMovieResource(dstMovie,dstMovieRefNum,
  510.                             &resID, outReply.fName );
  511.  
  512.         BailOnError(result);
  513.  
  514.         dstMedia = nil;
  515.     }
  516.  
  517.     /***************************************
  518.      *
  519.      *    Return the input filename to what it was originally
  520.      *
  521.      ***************************************/
  522.  
  523.       strcpy( (char *) inReply->fName, filename );
  524.      c2pstr( inReply->fName );
  525.  
  526.     /***************************************
  527.      *
  528.      *    Deallocate everything that was created.
  529.      *
  530.      ***************************************/
  531.      
  532.      cleanup();
  533.      
  534.      if (ci)
  535.         CloseComponent(ci);         
  536.         
  537.      return;
  538.  
  539. }
  540. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *
  541.  * cleanup:
  542.  *        Deallocate everything that was created.     If no error, 
  543.  *        continue, otherwise we're bailing out.
  544.  */
  545. cleanup()
  546. {
  547.  
  548.     if (dstMovieRefNum) {
  549.         CloseMovieFile(dstMovieRefNum);
  550.         dstMovieRefNum = 0;
  551.     }
  552.  
  553.     if (dstMovie) {
  554.         DisposeMovie(dstMovie);
  555.         dstMovie = nil;
  556.     }
  557.  
  558.     if (srcSeqID) {
  559.         CDSequenceEnd(srcSeqID);
  560.         srcSeqID = 0;
  561.     }
  562.     
  563.     if (dstSeqID) {
  564.         CDSequenceEnd(dstSeqID);
  565.         dstSeqID = 0;
  566.     }    
  567.  
  568.     if (srcResRefNum) {
  569.         CloseResFile(srcResRefNum);
  570.         srcResRefNum = 0;
  571.     }
  572.     
  573.     if (compressedData) {
  574.         DisposHandle(compressedData);
  575.         compressedData = nil;
  576.     }
  577.     
  578.     if (idh) {
  579.         DisposHandle((Handle)idh);
  580.         idh = nil;
  581.     }
  582.     
  583.     if (progressWindow) {
  584.         CloseWindow(progressWindow);
  585.         progressWindow = nil;
  586.     }
  587.     
  588.     if (pictGWorld) {
  589.         DisposeGWorld(pictGWorld);
  590.         pictGWorld = nil;
  591.     }
  592.  
  593.     if (result != 0)
  594.       abortive_error( result );
  595.  
  596.  
  597.   
  598. }     
  599. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  600.