/* * Copyright (c) 1997 by * PARALLAX GRAPHICS, INCORPORATED, Santa Clara, California. * All rights reserved * * This software is furnished on an as-is basis, and may be used and copied * only with the inclusion of the above copyright notice. * * The information in this software is subject to change without notice. * No committment is made as to the usability or reliability of this * software. * * Parallax Graphics, Inc. * 2500 Condensa Street * Santa Clara, California 95051 * Author: Scott Schmitz * * Adapted from SUN makemovie, substituting for jpeg library */ /* Program: makemovie Usage: makemovie [options] moviefile [options]: -fFrames Per Second -wWidth (in pixels) Height is automatic -sSeconds to record -qQfactor (quality of record) Example: makemovie -f25 -w320 -s15 -q100 mymovie This will record at 25 frames per second with a width of 320 pixels (height is automatic), for a length of 15 seconds. The quality will translate to about a 45:1 compression ratio. The file "mymovie" will contain a valid movie in Parallax "j_movie" format. Please refer to Appendix C of the Video Development Environment manual for a description of the format. Description: This demo illustrates the ability to use the xlib-level API to record a Parallax movie. The movie can be recorded and launched from a command-line, rather than using a GUI-based MovieTool. */ #include #include #include #include #include #include #include #include #include #include #include /* required for gettimeofday */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* function prototypes */ void print_help(void); long ElapsedTime(); char *mtFile; static int currentFrame; static int totalBytesInRecording; static int* recordFrameIndex = (int*)NULL; static int mtqfactor = 100; main(argc,argv) int argc; char **argv; { int depth; Display *disp; Window win; int screen, width=640, height=480, i=1; int winMask; /* Window attribute mask. */ XPlxCImage *cImage; GC gc; static unsigned char *qTable; /* Q Table to load to CCube chip */ static int qTableSize; /* Size of Q Table. */ XVisualInfo *vInfo; XSetWindowAttributes attr; static int mask=ExposureMask; /* Window event mask. */ XEvent winEvent; plx_signal *plxSignal=NULL; /* Video signal structure. */ int playbackRate=20; /* Movie playback rate, default to 20 fps, */ /* this is conservative. */ int QFactor=50; char jpegFile[30]; /* Movie file name. */ int end = 30; /* Number of frames to capture. */ int seconds = 10; /* Default to 10 seconds of recording */ long int us_per_frame; /* micro seconds per frame */ long int ms_per_frame; int frame = 0; struct timeval frametime, starttime, now; char *window_name="makemovie"; XTextProperty windowname; int nextFrameTime; int thisFrameTime; if (argc<2) /* Forgot to specify a filename? */ { /* Print help message */ print_help(); exit(1); } i = 1; while(i < argc && argv[i][0] == '-') /* Decode option list (options begin with */ /* a '-' */ { switch(argv[i][1]) { case 'f': /* set frames per second */ { playbackRate = atoi(argv[i]+2); printf("Record at %d fps.\n", playbackRate); } /* end of case 'f' */ break; case 's': /* set seconds to record */ { seconds = atoi(argv[i]+2); printf("Recording %d seconds.\n", seconds); } /* end of case 's' */ break; case 'w': /* set the width (height is automatic) */ { width = atoi(argv[i]+2); height = (int)((double)(480.0/640.0) * (double) width); printf("width = %d, height = %d (pixels).\n", width, height); } /* end of case 'w' */ break; case 'q': /* set the qfactor */ { QFactor = atoi(argv[i]+2); if(QFactor < 25 || QFactor > 1000) { QFactor = 50; printf("qfactor must be between 25 and 1000. "); } printf("qfactor = %d\n", QFactor); } /* end of case 'q' */ break; } /* end of switch statement */ i++; /* increment the argument pointer */ } /* end of while */ if(argv[argc - 1][0] == '-') /* The last parameter should not have a '-' */ { printf("parameter error\n"); printf("\n\n"); print_help(); return; } strcpy(jpegFile, argv[i]); mtFile = &jpegFile; us_per_frame = (long int)((1.0 / (float)playbackRate) * 1000000); ms_per_frame = us_per_frame / 1000; printf("%d ms_per_frame\n",ms_per_frame); disp=XOpenDisplay(getenv("DISPLAY")); screen=(int)XDefaultScreen(disp); vInfo = XPlxGetVideoVisual(screen, disp); /* Try to get a visual appropriate for showing live video */ depth = vInfo->depth; winMask=CWBackPixel|CWColormap|CWBorderPixel; /* Setup window attribute mask. */ attr.colormap=XCreateColormap(disp, /* Create colormap according to visual. */ XRootWindow(disp,screen), vInfo->visual,AllocNone); attr.background_pixel=BlackPixel(disp,screen); /* Set background color to black. */ attr.border_pixel=WhitePixel(disp,screen); /* Set boarder color to white. */ win=XCreateWindow(disp,RootWindow(disp,screen), /* Create window for frame grab. */ 0,0,width,height,0,depth,InputOutput, /* Window size is set to 640 x 480. */ vInfo->visual,winMask,&attr); XStringListToTextProperty(&window_name, 1, &windowname); XSetWMProperties(disp, win, &windowname, &windowname, argv, argc, NULL, NULL, NULL); gc=XCreateGC(disp, win, 0, 0); /* Create window gc. */ qTableSize=MakeQTables(QFactor, &qTable); /* Create Q Table according to QFactor. */ XPlxPutTable(disp,win,gc,(char*)qTable,qTableSize,0); /* Load QTable to chip for compression. */ XPlxVideoInputSelect(disp,win,gc, PLX_INPUT_0, PLX_NTSC, PLX_COMP, PLX_RGB24); /* Select video input type & channel. */ plxSignal=(plx_signal*)XPlxQueryVideo(disp, /* Detect for video signal at input. */ win,gc); while (!plxSignal->sync_ok) { printf("Sync Absent - Hook up an input source\n"); XPlxSleep(500000); plxSignal=(plx_signal*)XPlxQueryVideo(disp, win, gc); } XPlxVideoTag(disp,win,gc,PLX_VIDEO); /* Setup up window tag to display video */ XSelectInput(disp,win,mask); /* Setup up window event mask. */ XMapWindow(disp,win); /* Map window to display. */ XNextEvent(disp,&winEvent); recordFrameIndex = (int*)malloc(10000*sizeof(int)); createmtHeader(jpegFile,playbackRate,QFactor,width,height); XPlxVideoSqueezeLive(disp,win,gc,0,plxSignal->b, 640, 480, 0,0,width,height); /* Display live video. */ gettimeofday(&frametime, NULL); gettimeofday(&starttime, NULL); while(1) { gettimeofday(&now, NULL); nextFrameTime = (int)((unsigned int)(ms_per_frame * (frame+1)) - (unsigned int)(ElapsedTime(&starttime, &now))); printf("%d nextFrameTime %d 1st var %d 2nd var\n",nextFrameTime,((unsigned int)(ms_per_frame * frame+1)),((unsigned int)(ElapsedTime(&starttime, &now)))); if(nextFrameTime >= 0) /* Are we late for this frame? */ { XPlxSleep(nextFrameTime * 1000); /* No, grab a new image. */ gettimeofday(&frametime, NULL); cImage=XPlxGetCImage(disp,win, gc,0,0, width, height, width, height); /* Grab and compress image from window. */ } else { gettimeofday(&frametime, NULL); /* Yes, do not compress a new image. */ } appendtoMovie(cImage,frame); frame++; if(frame > seconds * playbackRate) { XPlxVideoStop(disp, win, gc); /* Stop the live video. */ XPlxDestroyCImage(cImage); appendIndex(frame); XFreeColormap(disp,attr.colormap); /* Free resources when done. */ XFreeGC(disp,gc); XDestroyWindow(disp,win); XFlush(disp); exit(0); } } } void print_help(void) { printf("usage: makemovie [-options] moviefile\n"); printf(" -options:\n"); printf(" -fFrames Per Second\n"); printf(" -qQfactor\n"); printf(" -wWidth (Height is automatic)\n"); printf(" -sSeconds\n"); return; } long ElapsedTime(before, after) struct timeval* before; struct timeval* after; { return(((after->tv_sec - before->tv_sec) * 1000) + /* Convert the seconds part to milliseconds */ ((after->tv_usec - before->tv_usec) /1000)); /* Convert the microseconds part to milliseconds */ } /* end function ElapsedTime */ createmtHeader(char* outputFileName,int fps,int Q,int wid, int ht) { int bytesWritten; int currentFrame; jpheader recordHeader; int outputFilenum; int Stillwidth; int Stillheight; outputFilenum = open(outputFileName, /* Try to open a file for writing */ (O_CREAT | O_RDWR | O_TRUNC), 0777); /* if it doesn't already exist */ if (outputFilenum == -1) /* Did the attempt fail? */ { /* Yes, report the error */ printf("Could not open the file %s for writing.\n", outputFileName); return; } currentFrame = 0; /* Start at the 1st frame of the new movie */ strcpy(recordHeader.magic, "j_movie"); recordHeader.version = JPEG_VERSION_2; /* Use latest version */ recordHeader.fps = fps; /* from cmd line */ recordHeader.frames = 0; /* When starting, no frames in the movie */ recordHeader.bandwidth = 0; /* Before, data rate of movie is unknown */ recordHeader.qfactor = Q; /* from cmd line */ recordHeader.width = wid; /* from cmd line */ recordHeader.height = ht; /* from cmd line */ recordHeader.mapsize = 256 * 256 * 256; /* The movie uses Truecolor; all 24 bits */ recordHeader.tracks = 0; /* No audio */ recordHeader.audioslice = 0; /* No audio */ recordHeader.audio.sample_rate=0; recordHeader.audio.samples_per_unit=0; recordHeader.audio.bytes_per_unit=0; recordHeader.audio.channels=0; recordHeader.audio.data_size=0; bytesWritten = write(outputFilenum, &recordHeader, sizeof(jpheader)); /* Write the movie header to disk */ if (bytesWritten < 0) /* Did the attempt fail? */ { /* Yes, report the error */ printf("Could not write anything to the movie file. Recording stopped.\n"); close(outputFilenum); return; } recordFrameIndex[currentFrame] = sizeof(jpheader); /* 1st frame of movie begins after header */ totalBytesInRecording = sizeof(jpheader); /* Update size of this movie */ close(outputFilenum); return; } /* end function create_header */ appendtoMovie(XPlxCImage *fullImage,int framenumber) { unsigned char instruction; int bytesWritten; int fd; int size; fd = open(mtFile, /* Try to open a file for writing */ (O_RDWR | O_APPEND)); if (fd == -1) /* Did the attempt fail? */ { /* Yes, report the error */ printf("Could not open the file %s for writing.\n", mtFile); return; } recordFrameIndex[framenumber]=totalBytesInRecording; instruction = LOAD_JPEG; /* Prepare to write JPEG info for frame */ bytesWritten = write(fd, &instruction, 1); /* Try to write the opcode to disk */ if (bytesWritten < 0) /* Did the attempt fail? */ { /* Yes, report the error */ printf("There was an error recording frame %d's JPEG image to disk. Recording stopped.\n",framenumber); return(True); /* Done recording */ } totalBytesInRecording += bytesWritten; /* Update size of this movie in bytes */ /* size=lseek(fd,0L,SEEK_END); /* lseek(fd,0,SEEK_SET); */ bytesWritten = write(fd, &(fullImage->size), sizeof(int)); /* Write size in bytes frame's JPEG data */ totalBytesInRecording += bytesWritten; /* Update size of this movie in bytes */ bytesWritten = write(fd, fullImage->data, /* Write this frame's JPEG image data */ fullImage->size); totalBytesInRecording += bytesWritten; /* Update size of this movie in bytes */ if (bytesWritten < sizeof(int)) /* Did the attempt fail? */ { /* Yes, report the error */ printf("There was an error recording this image's size information to disk. Recording stopped.\n"); return(True); /* Done recording */ } instruction = END_FRAME; /* Indicate end of this frame */ bytesWritten = write(fd, &instruction, 1); /* Try to write the opcode to disk */ if (bytesWritten < 0) /* Did the attempt fail? */ { /* Yes, report the error */ printf("There was an error writing the END_FRAME opcode to disk. Recording stopped.\n"); return(totalBytesInRecording); /* Done recording */ } totalBytesInRecording += bytesWritten; /* Update size of this movie in bytes */ close(fd); } /* end function appendtoMovie */ appendIndex(int currentFrame) { int bytesWritten; int fd; jpheader recordHeader; int i; fd = open(mtFile, /* Try to open a file for writing */ O_RDWR | O_APPEND); if (fd == -1) /* Did the attempt fail? */ { /* Yes, report the error */ printf("Could not open the file %s for writing.\n", mtFile); return; } bytesWritten = write(fd, /* Write frame index loc to the movie file */ recordFrameIndex, currentFrame * sizeof(int)); if (bytesWritten < 0) /* Did the attempt fail? */ { /* Yes, report the error */ printf("Could not write frame start location information to the movie file.\n"); close(fd); return; } close(fd); fd = open(mtFile, /* Try to open a file for writing */ O_RDWR); if (fd == -1) /* Did the attempt fail? */ { /* Yes, report the error */ printf("Could not open the file %s for writing.\n", mtFile); return; } read(fd,&recordHeader,sizeof(jpheader)); recordHeader.frames = currentFrame; /* Store the number of frames in movie */ recordHeader.firstValidFrame = 0; /* Mark the whole movie valid for playback */ recordHeader.lastValidFrame = currentFrame-1; recordHeader.indexbuf = totalBytesInRecording; /* Mark the location of the frame index */ recordHeader.bandwidth = /* Store the average data rate for movie */ ((totalBytesInRecording / currentFrame) * recordHeader.fps) / 1024; lseek(fd, 0, SEEK_SET); /* Go back to the start of movie to write */ bytesWritten = write(fd, &recordHeader, sizeof(jpheader)); /* movie header information. */ if (bytesWritten < 0) /* Did the attempt fail? */ { /* Yes, report the error */ printf("Could not write an updated movie header to the movie file.\n"); close(fd); return; } close(fd); /* Done with the output file; close it now */ return; } /* end function appendIndex */