This is Controller.m in view mode; [Download] [Up]
#import <math.h>
#import <strings.h>
#import <dpsclient/wraps.h>
#import <appkit/graphics.h>
#import <appkit/OpenPanel.h>
#import <appkit/NXImage.h>
#import <appkit/Application.h>
#import <appkit/Matrix.h>
#import <appkit/Form.h>
#import <appkit/NXBitmapImageRep.h>
#import <appkit/publicWraps.h>
#import <appkit/tiff.h>
#import <appkit/MenuCell.h>
#import <defaults/defaults.h>
#import <objc/NXBundle.h>
// Local Categories
#import "TimeCell.h"
// Local Objects
#import "Controller.h"
#import <sys/param.h>
#import <libc.h>
/* Globals */
char *gpchProgName = "MPEG Play";
char *gpchOK = "OK";
/* File extensions for MPEG files */
const char *mpgType = "mpg";
@implementation Controller:Object
/*** Instance methods ***/
- (mpegInfo *)getMPEGFrameSize:(NXSize *)fSize forFile:(const char *)mpegFile
{
char *pchAlertTitle =
"Error in Controller method -getMPEGFrameSize:forFile:";
FILE *input;
long data;
mpegInfo *pInfo;
// Check for NULL pointer or empty string
if (!mpegFile || !*mpegFile)
{
NXRunAlertPanel(pchAlertTitle, "No filename was specified.",
gpchOK, NULL, NULL);
return NULL;
}
if (NULL == (input = fopen(mpegFile, "r")))
{
NXRunAlertPanel(pchAlertTitle, "Could not open file - %s.",
gpchOK, NULL, NULL, mpegFile);
return NULL;
}
#define SEQ_START_CODE 0x000001b3
// read first start code, make sure it is a sequence start code
fread(&data, 4, 1, input);
if (SEQ_START_CODE != data)
{
NXRunAlertPanel(pchAlertTitle, "This is not an MPEG stream.",
gpchOK, NULL, NULL);
return NULL;
}
if (!(pInfo = calloc(1, sizeof(mpegInfo))))
{
NXRunAlertPanel(pchAlertTitle, "Unable to allocate memory.",
gpchOK, NULL, NULL);
return NULL;
}
// Get horizontal and vertical size of image space
// as two 12 bit words, respectively
// then aspect ratio and picture rate
// as two 4 bit words.
fread(&data, 4, 1, input);
pInfo->picture_rate = 0x0F & data;
data >>= 4;
pInfo->aspect_ratio = 0x0F & data;
data >>= 4;
// In Motorola format, least significant bits come last
// v_size is actually the second value in the file
// i.e. h:12,v:12,a:4,p:4
pInfo->v_size = 0x0FFF & data;
pInfo->h_size = 0x0FFF & data >> 12;
fSize->width = ((pInfo->h_size + 15) / 16) * 16.0;
fSize->height = ((pInfo->v_size + 15) / 16) * 16.0;
// Get bit rate, vbv buffer size, and constrained parameter flag
fread(&data, 4, 1, input);
// throw away (non) intra quant matrix flags
data >>= 2;
pInfo->const_param_flag = 1 & data;
data >>= 1;
pInfo->vbv_buffer_size = 0x03FF & data;
data >>= 10 + 1; // 1 marker bit
pInfo->bit_rate = 0x03FFFF & data;
fclose(input);
pInfo->fps = pInfo->picture_rate;
[self showInfo:pInfo];
return pInfo;
}
- showInfo:(mpegInfo *)pInfo
{
[panel disableDisplay];
// window settings
[colorSpaceMatrix selectCellWithTag:pInfo->colorSpace];
[zoomMatrix selectCellWithTag:pInfo->zoom];
[backingMatrix selectCellWithTag:pInfo->backing];
[buttonMatrix setState:pInfo->sync at:FLAG_SYNC :0];
[buttonMatrix setState:pInfo->drop at:FLAG_DROP :0];
[fpsForm setFloatValue:pInfo->fps at:0];
[fpsSlider setFloatValue:pInfo->fps];
// stream attributes
[attributesForm setIntValue:pInfo->h_size at:I_WIDTH];
[attributesForm setIntValue:pInfo->v_size at:I_HEIGHT];
[attributesForm setIntValue:pInfo->aspect_ratio at:I_ASPECT];
[attributesForm setIntValue:pInfo->picture_rate at:I_PICT_RATE];
[attributesForm setIntValue:pInfo->bit_rate at:I_BIT_RATE];
// measured statistics
[statsForm setIntValue:pInfo->totalFrames at:STATS_TOTAL];
[statsForm setIntValue:pInfo->frameCount at:STATS_FRAMES];
[[statsForm cellAt:STATS_ELAPSED :0] setTimeFloatValue:pInfo->elapsedTime];
[statsForm setFloatValue:((pInfo->frameCount + 1) / pInfo->elapsedTime)
at:STATS_FPS_RATE];
[[statsForm cellAt:STATS_ORIG_TIME :0]
setTimeFloatValue:(pInfo->frameCount / pInfo->picture_rate)];
[panel display]; // -reenableDisplay
return self;
}
- setFrameNumber:(int)frame
{
[form setIntValue:frame at:0];
return self;
}
- (int)frameNumber
{
return [form intValueAt:0];
}
/*** Instance variable access methods ***/
- setColorSpace:sender
{
colorSpace = [[sender selectedCell] tag];
return self;
}
- (int)colorSpace
{
return colorSpace;
}
- setZoom:sender
{
id window = [NXApp mainWindow];
id contentView = [window contentView];
zoom = [[sender selectedCell] tag];
if ([contentView respondsTo:@selector(newZoom:)])
{
NXSize *pSize = [contentView newZoom:zoom];
[window sizeWindow:(pSize->width * zoom) :(pSize->height * zoom)];
[window center];
[self windowDidMove:window];
[contentView display];
[contentView banner];
}
return self;
}
- (int)zoom
{
return zoom;
}
- setBacking:sender
{
backing = [[sender selectedCell] tag];
[[NXApp mainWindow] setBackingType:backing];
return self;
}
- setFlag:sender
{
id view = [[NXApp mainWindow] contentView];
id button = [sender selectedCell];
BOOL flag = [button state];
switch ([button tag])
{
case FLAG_SYNC:
if ([view respondsTo:@selector(newSync:)])
[view newSync:(sync = flag)];
break;
case FLAG_DROP:
if ([view respondsTo:@selector(newDrop:)])
[view newDrop:(drop = flag)];
break;
default:
return nil;
}
return self;
}
- setFPS:sender
{
id view = [[NXApp mainWindow] contentView];
float value = [sender floatValue];
if (sender == fpsSlider)
[fpsForm setFloatValue:value at:0];
else
[fpsSlider setFloatValue:value];
if ([view respondsTo:@selector(newFrameRate:)])
[view newFrameRate:value];
return self;
}
/*** Target/Action methods ***/
- openRequest:sender
{
id openReq;
const char *const fileTypes[] = { mpgType, NULL };
openReq = [OpenPanel new];
if ([openReq runModalForTypes:fileTypes])
{
int ok;
[NXApp openFile:[openReq filename] ok:&ok];
}
return self;
}
- playAgain:sender
{
id view = [[NXApp mainWindow] contentView];
if ([view respondsTo:@selector(runAgain)])
[view runAgain];
return self;
}
- updateInfoPanels:sender
{
mpegInfo *pInfo = [sender info];
[playAgainMenuItem setEnabled:[sender ready]];
pInfo->backing = [[sender window] backingType];
return [self showInfo:pInfo];
}
/*** Application delegate methods ***/
- appWillInit:sender
{
[colorSpaceMatrix selectCellWithTag:(colorSpace = RGB_COLOR)];
[zoomMatrix selectCellWithTag:(zoom = 1)];
[backingMatrix selectCellWithTag:(backing = NX_BUFFERED)];
[buttonMatrix setState:(sync = NO) at:FLAG_SYNC :0];
[buttonMatrix setState:(drop = NO) at:FLAG_DROP :0];
return self;
}
/* We always want to accept files. */
- (BOOL)appAcceptsAnotherFile:sender
{
return YES;
}
/* Open mpg file from workspace. */
// -appOpenFile:type: is defunct and does not appear in the headers
- (int)app:sender openFile:(const char *)filename type:(const char *)aType
{
id window, view;
mpegInfo *pInfo;
NXSize size;
NXRect rect;
if (strcmp(aType, mpgType))
{
NXRunAlertPanel("Error Opening File",
"Must have `.mpg' extension. Cannot open %s.",
gpchOK, NULL, NULL, filename);
return NO;
}
if (NULL == (pInfo = [self getMPEGFrameSize:&size forFile:filename]))
{
NXRunAlertPanel("Error Parsing MPEG File",
"Could not determine frame size for file %s.",
gpchOK, NULL, NULL, filename);
return NO;
}
pInfo->colorSpace = colorSpace;
pInfo->zoom = zoom;
pInfo->backing = backing;
pInfo->sync = sync;
pInfo->drop = drop;
if (nil == (view = [[MPEGView alloc] initSize:(const NXSize *)&size
info:pInfo]))
{
NXRunAlertPanel("Error Allocating View Object",
"Could not allocate and initialize an instance of MPEGView "
"with the following attributes: Width=%d, Height=%d, Zoom=%d",
gpchOK, NULL, NULL, size.width, size.height, zoom);
free(pInfo);
return NO;
}
/* Create a new window the size of the image */
NXSetRect(&rect, 0.0, 0.0, size.width * zoom, size.height * zoom);
window = [[Window alloc] initContent:&rect
style:NX_TITLEDSTYLE
backing:backing
buttonMask:NX_CLOSEBUTTONMASK
defer:NO];
[window setDelegate:self];
[window setTitleAsFilename:filename];
/* Replace the current contentView of the window with my new view */
/* and free up the old contentView */
[[window setContentView:view] free];
[window center]; /* Put the window in the center of the screen */
// -center does not call -windowDidMove: ... so do it now
[self windowDidMove:window];
[window makeKeyAndOrderFront:self];
[window display];
[view runFromFile:filename];
return YES;
}
/*** Window delegate methods ***/
- windowDidMove:sender
{
NXRect rect;
[sender getFrame:&rect];
// restrict left edge to 8 pixel boundary to enhance drawing speed
[sender moveTo:(8.0 * rint(rect.origin.x / 8)) :rect.origin.y];
return self;
}
- windowWillClose:sender
{
// this won't work because Menus are counted as Windows
if (!([[NXApp windowList] count]))
[playAgainMenuItem setEnabled:NO];
return self;
}
- windowDidBecomeMain:sender
{
id view = [sender contentView];
if ([view respondsTo:@selector(info)])
[self updateInfoPanels:view];
return self;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.