home *** CD-ROM | disk | FTP | other *** search
/ Altsys Virtuoso 2.0K / virtuoso_20k.iso / DemoApps / Graphics / Multimedia / MPEGPlay2.3 / Source / Controller.m < prev    next >
Encoding:
Text File  |  1993-03-09  |  8.8 KB  |  383 lines

  1.  
  2. #import <math.h>
  3. #import <strings.h>
  4. #import <dpsclient/wraps.h>
  5. #import <appkit/graphics.h>
  6. #import <appkit/OpenPanel.h>
  7. #import <appkit/NXImage.h>
  8. #import <appkit/Application.h>
  9. #import <appkit/Matrix.h>
  10. #import <appkit/Form.h>
  11. #import <appkit/NXBitmapImageRep.h>
  12. #import <appkit/publicWraps.h>
  13. #import <appkit/tiff.h>
  14. #import <appkit/MenuCell.h>
  15. #import <defaults/defaults.h>
  16. #import <objc/NXBundle.h>
  17. // Local Categories
  18. #import "TimeCell.h"
  19. // Local Objects
  20. #import "Controller.h"
  21.  
  22.  
  23. #import <sys/param.h>
  24. #import <libc.h>
  25.  
  26. /* Globals */
  27. char *gpchProgName = "MPEG Play";
  28. char *gpchOK = "OK";
  29.  
  30. /* File extensions for MPEG files */
  31. const char *mpgType = "mpg";
  32.  
  33.  
  34. @implementation Controller:Object
  35.  
  36. /*** Instance methods ***/
  37.  
  38. - (mpegInfo *)getMPEGFrameSize:(NXSize *)fSize forFile:(const char *)mpegFile
  39. {
  40.     char *pchAlertTitle =
  41.             "Error in Controller method -getMPEGFrameSize:forFile:";
  42.     FILE *input;
  43.     long data;
  44.     mpegInfo *pInfo;
  45.  
  46.     // Check for NULL pointer or empty string
  47.     if (!mpegFile || !*mpegFile)
  48.     {
  49.         NXRunAlertPanel(pchAlertTitle, "No filename was specified.",
  50.                 gpchOK, NULL, NULL);
  51.         return NULL;
  52.     }
  53.     if (NULL == (input = fopen(mpegFile, "r")))
  54.     {
  55.         NXRunAlertPanel(pchAlertTitle, "Could not open file - %s.",
  56.                 gpchOK, NULL, NULL, mpegFile);
  57.         return NULL;
  58.     }
  59. #define SEQ_START_CODE 0x000001b3
  60.     // read first start code, make sure it is a sequence start code
  61.     fread(&data, 4, 1, input);
  62.     if (SEQ_START_CODE != data)
  63.     {
  64.         NXRunAlertPanel(pchAlertTitle, "This is not an MPEG stream.",
  65.                 gpchOK, NULL, NULL);
  66.         return NULL;
  67.     }
  68.     if (!(pInfo = calloc(1, sizeof(mpegInfo))))
  69.     {
  70.         NXRunAlertPanel(pchAlertTitle, "Unable to allocate memory.",
  71.                 gpchOK, NULL, NULL);
  72.         return NULL;
  73.     }
  74.     // Get horizontal and vertical size of image space
  75.     // as two 12 bit words, respectively
  76.     // then aspect ratio and picture rate
  77.     // as two 4 bit words.
  78.     fread(&data, 4, 1, input);
  79.     pInfo->picture_rate = 0x0F & data;
  80.     data >>= 4;
  81.     pInfo->aspect_ratio = 0x0F & data;
  82.     data >>= 4;
  83.     // In Motorola format, least significant bits come last
  84.     // v_size is actually the second value in the file
  85.     // i.e. h:12,v:12,a:4,p:4
  86.     pInfo->v_size = 0x0FFF & data;
  87.     pInfo->h_size = 0x0FFF & data >> 12;
  88.     fSize->width = ((pInfo->h_size + 15) / 16) * 16.0;
  89.     fSize->height = ((pInfo->v_size + 15) / 16) * 16.0;
  90.     // Get bit rate, vbv buffer size, and constrained parameter flag
  91.     fread(&data, 4, 1, input);
  92.     // throw away (non) intra quant matrix flags
  93.     data >>= 2;
  94.     pInfo->const_param_flag = 1 & data;
  95.     data >>= 1;
  96.     pInfo->vbv_buffer_size = 0x03FF & data;
  97.     data >>= 10 + 1;    // 1 marker bit
  98.     pInfo->bit_rate = 0x03FFFF & data;
  99.     fclose(input);
  100.     pInfo->fps = pInfo->picture_rate;
  101.     [self showInfo:pInfo];
  102.     return pInfo;
  103. }
  104.  
  105.  
  106. - showInfo:(mpegInfo *)pInfo
  107. {
  108.     [panel disableDisplay];
  109.     // window settings
  110.     [colorSpaceMatrix selectCellWithTag:pInfo->colorSpace];
  111.     [zoomMatrix selectCellWithTag:pInfo->zoom];
  112.     [backingMatrix selectCellWithTag:pInfo->backing];
  113.     [buttonMatrix setState:pInfo->sync at:FLAG_SYNC :0];
  114.     [buttonMatrix setState:pInfo->drop at:FLAG_DROP :0];
  115.     [fpsForm setFloatValue:pInfo->fps at:0];
  116.     [fpsSlider setFloatValue:pInfo->fps];
  117.     // stream attributes
  118.     [attributesForm setIntValue:pInfo->h_size at:I_WIDTH];
  119.     [attributesForm setIntValue:pInfo->v_size at:I_HEIGHT];
  120.     [attributesForm setIntValue:pInfo->aspect_ratio at:I_ASPECT];
  121.     [attributesForm setIntValue:pInfo->picture_rate at:I_PICT_RATE];
  122.     [attributesForm setIntValue:pInfo->bit_rate at:I_BIT_RATE];
  123.     // measured statistics
  124.     [statsForm setIntValue:pInfo->totalFrames at:STATS_TOTAL];
  125.     [statsForm setIntValue:pInfo->frameCount at:STATS_FRAMES];
  126.     [[statsForm cellAt:STATS_ELAPSED :0] setTimeFloatValue:pInfo->elapsedTime];
  127.     [statsForm setFloatValue:((pInfo->frameCount + 1) / pInfo->elapsedTime)
  128.             at:STATS_FPS_RATE];
  129.     [[statsForm cellAt:STATS_ORIG_TIME :0]
  130.             setTimeFloatValue:(pInfo->frameCount / pInfo->picture_rate)];
  131.     [panel display];    // -reenableDisplay
  132.     return self;
  133. }
  134.  
  135.  
  136. - setFrameNumber:(int)frame
  137. {
  138.     [form setIntValue:frame at:0];
  139.     return self;
  140. }
  141.  
  142. - (int)frameNumber
  143. {
  144.     return [form intValueAt:0];
  145. }
  146.  
  147.  
  148. /*** Instance variable access methods ***/
  149.  
  150. - setColorSpace:sender
  151. {
  152.     colorSpace = [[sender selectedCell] tag];
  153.     return self;
  154. }
  155.  
  156. - (int)colorSpace
  157. {
  158.     return colorSpace;
  159. }
  160.  
  161.  
  162. - setZoom:sender
  163. {
  164.     id window = [NXApp mainWindow];
  165.     id contentView = [window contentView];
  166.  
  167.     zoom = [[sender selectedCell] tag];
  168.     if ([contentView respondsTo:@selector(newZoom:)])
  169.     {
  170.         NXSize *pSize = [contentView newZoom:zoom];
  171.  
  172.         [window sizeWindow:(pSize->width * zoom) :(pSize->height * zoom)];
  173.         [window center];
  174.         [self windowDidMove:window];
  175.         [contentView display];
  176.         [contentView banner];
  177.     }
  178.     return self;
  179. }
  180.  
  181. - (int)zoom
  182. {
  183.     return zoom;
  184. }
  185.  
  186.  
  187. - setBacking:sender
  188. {
  189.     backing = [[sender selectedCell] tag];
  190.     [[NXApp mainWindow] setBackingType:backing];
  191.     return self;
  192. }
  193.  
  194.  
  195. - setFlag:sender
  196. {
  197.     id view = [[NXApp mainWindow] contentView];
  198.     id button = [sender selectedCell];
  199.     BOOL flag = [button state];
  200.  
  201.     switch ([button tag])
  202.     {
  203.     case FLAG_SYNC:
  204.         if ([view respondsTo:@selector(newSync:)])
  205.             [view newSync:(sync = flag)];
  206.         break;
  207.     case FLAG_DROP:
  208.         if ([view respondsTo:@selector(newDrop:)])
  209.             [view newDrop:(drop = flag)];
  210.         break;
  211.     default:
  212.         return nil;
  213.     }
  214.     return self;
  215. }
  216.  
  217.  
  218. - setFPS:sender
  219. {
  220.     id view = [[NXApp mainWindow] contentView];
  221.     float value = [sender floatValue];
  222.  
  223.     if (sender == fpsSlider)
  224.         [fpsForm setFloatValue:value at:0];
  225.     else
  226.         [fpsSlider setFloatValue:value];
  227.     if ([view respondsTo:@selector(newFrameRate:)])
  228.         [view newFrameRate:value];
  229.     return self;
  230. }
  231.  
  232.  
  233. /*** Target/Action methods ***/
  234.  
  235. - openRequest:sender
  236. {
  237.     id         openReq;
  238.     const char    *const fileTypes[] = { mpgType, NULL };
  239.     
  240.     openReq = [OpenPanel new];
  241.     if ([openReq runModalForTypes:fileTypes])
  242.     {
  243.         int ok;
  244.  
  245.         [NXApp openFile:[openReq filename] ok:&ok];
  246.     }
  247.     return self;
  248. }
  249.  
  250. - playAgain:sender
  251. {
  252.     id view = [[NXApp mainWindow] contentView];
  253.  
  254.     if ([view respondsTo:@selector(runAgain)])
  255.         [view runAgain];
  256.     return self;
  257. }
  258.  
  259. - updateInfoPanels:sender
  260. {
  261.     mpegInfo *pInfo = [sender info];
  262.  
  263.     [playAgainMenuItem setEnabled:[sender ready]];
  264.     pInfo->backing = [[sender window] backingType];
  265.     return [self showInfo:pInfo];
  266. }
  267.  
  268.  
  269. /*** Application delegate methods ***/
  270.  
  271. - appWillInit:sender
  272. {
  273.     [colorSpaceMatrix selectCellWithTag:(colorSpace = RGB_COLOR)];
  274.     [zoomMatrix selectCellWithTag:(zoom = 1)];
  275.     [backingMatrix selectCellWithTag:(backing = NX_BUFFERED)];
  276.     [buttonMatrix setState:(sync = NO) at:FLAG_SYNC :0];
  277.     [buttonMatrix setState:(drop = NO) at:FLAG_DROP :0];
  278.     return self;
  279. }
  280.  
  281.  
  282. /* We always want to accept files. */
  283. - (BOOL)appAcceptsAnotherFile:sender
  284. {
  285.     return YES;
  286. }
  287.  
  288.  
  289. /* Open mpg file from workspace. */
  290. // -appOpenFile:type: is defunct and does not appear in the headers
  291. - (int)app:sender openFile:(const char *)filename type:(const char *)aType
  292. {
  293.     id    window, view;
  294.     mpegInfo *pInfo;
  295.     NXSize    size;
  296.     NXRect    rect;
  297.  
  298.     if (strcmp(aType, mpgType))
  299.     {
  300.         NXRunAlertPanel("Error Opening File",
  301.                 "Must have `.mpg' extension.  Cannot open %s.",
  302.                 gpchOK, NULL, NULL, filename);
  303.         return NO;
  304.     }
  305.     if (NULL == (pInfo = [self getMPEGFrameSize:&size forFile:filename]))
  306.     {
  307.         NXRunAlertPanel("Error Parsing MPEG File",
  308.                 "Could not determine frame size for file %s.",
  309.                 gpchOK, NULL, NULL, filename);
  310.         return NO;
  311.     }
  312.     pInfo->colorSpace = colorSpace;
  313.     pInfo->zoom = zoom;
  314.     pInfo->backing = backing;
  315.     pInfo->sync = sync;
  316.     pInfo->drop = drop;
  317.     if (nil == (view = [[MPEGView alloc] initSize:(const NXSize *)&size
  318.                 info:pInfo]))
  319.     {
  320.         NXRunAlertPanel("Error Allocating View Object",
  321.                 "Could not allocate and initialize an instance of MPEGView "
  322.                 "with the following attributes: Width=%d, Height=%d, Zoom=%d",
  323.                 gpchOK, NULL, NULL, size.width, size.height, zoom);
  324.         free(pInfo);
  325.         return NO;
  326.     }
  327.     /* Create a new window the size of the image */
  328.     NXSetRect(&rect, 0.0, 0.0, size.width * zoom, size.height * zoom);
  329.     window = [[Window alloc] initContent:&rect
  330.             style:NX_TITLEDSTYLE
  331.             backing:backing
  332.             buttonMask:NX_CLOSEBUTTONMASK
  333.             defer:NO];
  334.     [window setDelegate:self];
  335.     [window setTitleAsFilename:filename];
  336.  
  337.     /* Replace the current contentView of the window with my new view */
  338.     /*    and free up the old contentView */
  339.     [[window setContentView:view] free];
  340.     [window center];    /* Put the window in the center of the screen */
  341.     // -center does not call -windowDidMove: ... so do it now
  342.     [self windowDidMove:window];
  343.     [window makeKeyAndOrderFront:self];
  344.     [window display];
  345.     [view runFromFile:filename];
  346.     return YES;
  347. }
  348.  
  349.  
  350. /*** Window delegate methods ***/
  351.  
  352. - windowDidMove:sender
  353. {
  354.     NXRect    rect;
  355.  
  356.     [sender getFrame:&rect];
  357.     // restrict left edge to 8 pixel boundary to enhance drawing speed
  358.     [sender moveTo:(8.0 * rint(rect.origin.x / 8)) :rect.origin.y];
  359.     return self;
  360. }
  361.  
  362.  
  363. - windowWillClose:sender
  364. {
  365.     // this won't work because Menus are counted as Windows
  366.     if (!([[NXApp windowList] count]))
  367.         [playAgainMenuItem setEnabled:NO];
  368.     return self;
  369. }
  370.  
  371.  
  372. - windowDidBecomeMain:sender
  373. {
  374.     id view = [sender contentView];
  375.  
  376.     if ([view respondsTo:@selector(info)])
  377.         [self updateInfoPanels:view];
  378.     return self;
  379. }
  380.  
  381.  
  382. @end
  383.