/* * 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: dblmovie Usage: dblmovie [options] moviefile [options]: -fFrames Per Second -wWidth (in pixels) Height is automatic -sSeconds to record -qQfactor to record at Example: dblmovie -f25 -w320 -s15 -q100 movie This will record at 25 frames per second with a width of 320 pixels (height is automatic), for a length of 15 seconds, and at a Q factor of 100. The files "movie" and "movieb" will be created. Description: This demo illustrate the usage of XVideo Movie API to make two JPEG movies from live sources. It captures and compresses frames of video and puts it in the JPEG movie format. Audio is not implemented in this demo. */ #include #include #include #include #include #include #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; /* DBL */ char *mtFile2; static int currentFrame; static int totalBytesInRecording; /* DBL */ static int totalBytesInRecording2; static int* recordFrameIndex = (int*)NULL; /* DBL */ static int* recordFrameIndex2 = (int*)NULL; static int mtqfactor = 100; main(argc,argv) int argc; char **argv; { int depth; Display *disp; Window win; /* DBL */ Window win2; int screen, width=640, height=480, i=1; int winMask; /* Window attribute mask. */ XPlxCImage *cImage = NULL; /* DBL */ XPlxCImage *cImage2 = NULL; XShmSegmentInfo * shminfo; XShmSegmentInfo * shminfo2; Status status; GC gc; /* DBL */ GC gc2; 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. */ /* DBL */ char jpegFile2[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, endtime; 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; sprintf(jpegFile2,"%s%s",mtFile,"b"); mtFile2 = &jpegFile2; us_per_frame = (long int)((1.0 / (float)playbackRate) * 1000000); ms_per_frame = us_per_frame / 1000; 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); /* DBL */ win2=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. */ /* DBL */ gc2=XCreateGC(disp, win2, 0, 0); /* Create window2 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. */ /* DBL */ XPlxPutTable(disp,win2,gc2,(char*)qTable,qTableSize,0); /* Load QTable to chip for compression. */ XPlxVideoInputSelect(disp,win,gc, /* Input Composite 1 on Channel 1 */ PLX_INPUT_0, PLX_NTSC, PLX_COMP, PLX_RGB24); /* DBL */ XPlxVideoInputSelect(disp,win2,gc2, /* Input Composite 2 on Channel 2 */ PLX_INPUT_1, PLX_NTSC, PLX_COMP, 1); 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); } XSelectInput(disp,win,mask); /* Setup up window event mask. */ /* DBL */ XSelectInput(disp,win2,mask); /* Setup up window event mask. */ XMapWindow(disp,win); /* Map window to display. */ /* DBL */ XMapWindow(disp,win2); /* Map window to display. */ XMoveWindow(disp,win,0,0); XMoveWindow(disp,win2,330,0); XNextEvent(disp,&winEvent); recordFrameIndex = (int*)malloc(10000*sizeof(int)); recordFrameIndex2 = (int*)malloc(10000*sizeof(int)); createmtHeader(jpegFile,playbackRate,QFactor,width,height); createmt2Header(jpegFile2,playbackRate,QFactor,width,height); XPlxVideoSqueezeLive(disp,win,gc,0,plxSignal->b, 640, 480, 0,0,width,height); /* Display live video. */ /* DBL */ XPlxVideoSqueezeLive(disp,win2,gc2,0,plxSignal->b, 640, 480, 0,0,width,height); /* Display live video. */ /* gettimeofday(&frametime, NULL); */ shminfo = XPlxGetShmInfo(); shminfo2 = XPlxGetShmInfo(); cImage = (XPlxCImage *)XPlxShmCreateCImage(disp, shminfo->shmaddr,shminfo,0,0,0); cImage2 = (XPlxCImage *)XPlxShmCreateCImage(disp, shminfo2->shmaddr,shminfo2,0,0,0); shminfo->readOnly = False; shminfo2->readOnly = False; status = XPlxShmAttach(disp, shminfo); status = XPlxShmAttach(disp, shminfo2); gettimeofday(&starttime, NULL); gettimeofday(&endtime, NULL); while(1) { gettimeofday(&now, NULL); nextFrameTime = (int)((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->width = width; cImage->height = height; XPlxShmGetCImage(disp,win, gc,cImage,0,0, width, height); /* Grab and compress image from window. */ gettimeofday(&now, NULL); cImage2->width = width; cImage2->height = height; XPlxShmGetCImage(disp,win2, gc2,cImage2,0,0, width, height); /* Grab and compress image from 2nd window. */ gettimeofday(&endtime, NULL); } else { gettimeofday(&frametime, NULL); /* Yes, do not compress a new image. */ } appendtoMovie(cImage,frame); appendtoMovie2(cImage2,frame); frame++; if(frame > seconds * playbackRate) { gettimeofday(&now, NULL); XPlxVideoStop(disp, win, gc); /* Stop the live video. */ XPlxVideoStop(disp, win2, gc2); /* Stop the live video. */ appendIndex(frame); appendIndex2(frame); XFreeColormap(disp,attr.colormap); /* Free resources when done. */ XPlxShmDetach(disp, shminfo); XPlxShmDetach(disp, shminfo2); XPlxFreeShmInfo(cImage->info); XPlxFreeShmInfo(cImage2->info); XFreeGC(disp,gc); XFreeGC(disp,gc2); XDestroyWindow(disp,win); XDestroyWindow(disp,win2); 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 */ createmt2Header(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; } recordFrameIndex2[currentFrame] = sizeof(jpheader); /* 1st frame of movie begins after header */ totalBytesInRecording2 = sizeof(jpheader); /* Update size of this movie */ close(outputFilenum); return; } /* end function create_header2 */ 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 */ appendtoMovie2(XPlxCImage *fullImage,int framenumber) { unsigned char instruction; int bytesWritten; int fd; int size; fd = open(mtFile2, /* 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; } recordFrameIndex2[framenumber]=totalBytesInRecording2; 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 */ } totalBytesInRecording2 += 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 */ totalBytesInRecording2 += bytesWritten; /* Update size of this movie in bytes */ bytesWritten = write(fd, fullImage->data, /* Write this frame's JPEG image data */ fullImage->size); totalBytesInRecording2 += 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(totalBytesInRecording2); /* Done recording */ } totalBytesInRecording2 += bytesWritten; /* Update size of this movie in bytes */ close(fd); } /* end function appendtoMovie2 */ 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 */ appendIndex2(int currentFrame) { int bytesWritten; int fd; jpheader recordHeader; int i; fd = open(mtFile2, /* 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 */ recordFrameIndex2, 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(mtFile2, /* 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 = totalBytesInRecording2; /* Mark the location of the frame index */ recordHeader.bandwidth = /* Store the average data rate for movie */ ((totalBytesInRecording2 / 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 appendIndex2 */