diff options
Diffstat (limited to 'converter/ppm/ppmtompeg/ppmtompeg.c')
-rw-r--r-- | converter/ppm/ppmtompeg/ppmtompeg.c | 722 |
1 files changed, 722 insertions, 0 deletions
diff --git a/converter/ppm/ppmtompeg/ppmtompeg.c b/converter/ppm/ppmtompeg/ppmtompeg.c new file mode 100644 index 00000000..2296e2cb --- /dev/null +++ b/converter/ppm/ppmtompeg/ppmtompeg.c @@ -0,0 +1,722 @@ +/*===========================================================================* + * main.c + * + * Main procedure + * + *===========================================================================*/ + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +/*==============* + * HEADER FILES * + *==============*/ + +#define _BSD_SOURCE /* Make sure strdup() is in string.h */ + +#include <assert.h> +#include <sys/utsname.h> + +#include "all.h" +#include "mtypes.h" +#include "mpeg.h" +#include "motion_search.h" +#include "prototypes.h" +#include "param.h" +#include "parallel.h" +#include "readframe.h" +#include "combine.h" +#include "frames.h" +#include "jpeg.h" +#include "specifics.h" +#include "opts.h" +#include "frametype.h" +#include "input.h" +#include "gethostname.h" + +#include "pm_c_util.h" +#include "ppm.h" +#include "nstring.h" + +#include <time.h> + +int main _ANSI_ARGS_((int argc, char **argv)); + + +/*==================* + * GLOBAL VARIABLES * + *==================*/ + +boolean showBitRatePerFrame; +boolean frameSummary; + +extern time_t IOtime; +int whichGOP = -1; +boolean ioServer = FALSE; +boolean outputServer = FALSE; +boolean decodeServer = FALSE; +int quietTime = 0; +boolean realQuiet = FALSE; +boolean noFrameSummaryOption = FALSE; +boolean debugSockets = FALSE; +boolean debugMachines = FALSE; +boolean bitRateInfoOption = FALSE; +boolean computeMVHist = FALSE; +int baseFormat; +extern boolean specificsOn; +extern FrameSpecList *fsl; +boolean pureDCT=FALSE; +char encoder_name[1024]; +const char * hostname; + + +/*================================* + * External PROCEDURE prototypes * + *================================*/ + +void init_idctref _ANSI_ARGS_((void)); +void init_fdct _ANSI_ARGS_((void)); + + +struct cmdlineInfo { + bool childProcess; + int function; + const char * masterHostname; + int masterPortNumber; + unsigned int outputFrames; + int maxMachines; + const char * paramFileName; + bool specificFrames; + unsigned int frameStart; + unsigned int frameEnd; +}; + + + +static void +parseArgs(int const argc, + char ** const argv, + struct cmdlineInfo * const cmdlineP) { + + int idx; + + if (argc-1 < 1) + pm_error("You must specify at least one argument: the parameter " + "file name"); + + cmdlineP->function = ENCODE_FRAMES; + cmdlineP->childProcess = FALSE; /* initial assumption */ + cmdlineP->outputFrames = 0; + cmdlineP->maxMachines = MAXINT; + cmdlineP->specificFrames = FALSE; + + /* parse the arguments */ + idx = 1; + while ( idx < argc-1 ) { + if ( argv[idx][0] != '-' ) + pm_error("argument '%s', which must be an option because " + "it is not the last argument, " + "does not start with '-'", argv[idx]); + + if ( strcmp(argv[idx], "-stat") == 0 ) { + if ( idx+1 < argc-1 ) { + SetStatFileName(argv[idx+1]); + idx += 2; + } else { + pm_error("Invalid -stat option"); + } + } else if ( strcmp(argv[idx], "-gop") == 0 ) { + if ((cmdlineP->function != ENCODE_FRAMES) || + (cmdlineP->specificFrames)) + pm_error("Invalid -gop option"); + + if ( idx+1 < argc-1 ) { + whichGOP = atoi(argv[idx+1]); + idx += 2; + } else { + pm_error("Invalid -gop option"); + } + } else if ( strcmp(argv[idx], "-frames") == 0 ) { + if ( (cmdlineP->function != ENCODE_FRAMES) || (whichGOP != -1) ) { + pm_error("invalid -frames option"); + } + + if ( idx+2 < argc-1 ) { + int const frameStart = atoi(argv[idx+1]); + int const frameEnd = atoi(argv[idx+2]); + + if (frameStart > frameEnd) + pm_error("Start frame number %d is greater than end " + "frame number %d", frameStart, frameEnd); + if (frameStart < 0) + pm_error("Start frame number %d is less than zero", + frameStart); + + cmdlineP->specificFrames = TRUE; + cmdlineP->frameStart = frameStart; + cmdlineP->frameEnd = frameEnd; + + idx += 3; + } else + pm_error("-frames needs to be followed by two values"); + } else if (strcmp(argv[idx], "-combine_gops") == 0) { + if ((cmdlineP->function != ENCODE_FRAMES) || (whichGOP != -1) || + (cmdlineP->specificFrames)) { + pm_error("Invalid -combine_gops option"); + } + + cmdlineP->function = COMBINE_GOPS; + idx++; + } else if (strcmp(argv[idx], "-combine_frames") == 0) { + if ((cmdlineP->function != ENCODE_FRAMES) || (whichGOP != -1) || + (cmdlineP->specificFrames)) + pm_error("Invalid -combine_frames option"); + + cmdlineP->function = COMBINE_FRAMES; + idx++; + } else if ( strcmp(argv[idx], "-child") == 0 ) { + if ( idx+7 < argc-1 ) { + int combinePortNumber; + /* This used to be important information, when the child + notified the combine server. Now the master notifies + the combine server after the child notifies the master + it is done. So this value is unused. + */ + cmdlineP->masterHostname = argv[idx+1]; + cmdlineP->masterPortNumber = atoi(argv[idx+2]); + ioPortNumber = atoi(argv[idx+3]); + combinePortNumber = atoi(argv[idx+4]); + decodePortNumber = atoi(argv[idx+5]); + machineNumber = atoi(argv[idx+6]); + remoteIO = atoi(argv[idx+7]); + + IOhostName = cmdlineP->masterHostname; + } else + pm_error("Not enough option values for -child option. " + "Need 7."); + + cmdlineP->childProcess = TRUE; + idx += 8; + } else if ( strcmp(argv[idx], "-io_server") == 0 ) { + if ( idx+2 < argc-1 ) { + cmdlineP->masterHostname = argv[idx+1]; + cmdlineP->masterPortNumber = atoi(argv[idx+2]); + } else { + pm_error("Invalid -io_server option"); + } + + ioServer = TRUE; + idx += 3; + } else if ( strcmp(argv[idx], "-output_server") == 0 ) { + if ( idx+3 < argc-1 ) { + cmdlineP->masterHostname = argv[idx+1]; + cmdlineP->masterPortNumber = atoi(argv[idx+2]); + cmdlineP->outputFrames = atoi(argv[idx+3]); + } else { + pm_error("-output_server option requires 3 option values. " + "You specified %d", argc-1 - idx); + } + + outputServer = TRUE; + idx += 4; + } else if ( strcmp(argv[idx], "-decode_server") == 0 ) { + if ( idx+3 < argc-1 ) { + cmdlineP->masterHostname = argv[idx+1]; + cmdlineP->masterPortNumber = atoi(argv[idx+2]); + cmdlineP->outputFrames = atoi(argv[idx+3]); + } else { + pm_error("Invalid -decode_server option"); + } + + cmdlineP->function = COMBINE_FRAMES; + decodeServer = TRUE; + idx += 4; + } else if ( strcmp(argv[idx], "-nice") == 0 ) { + niceProcesses = TRUE; + idx++; + } else if ( strcmp(argv[idx], "-max_machines") == 0 ) { + if ( idx+1 < argc-1 ) { + cmdlineP->maxMachines = atoi(argv[idx+1]); + } else { + pm_error("Invalid -max_machines option"); + } + + idx += 2; + } else if ( strcmp(argv[idx], "-quiet") == 0 ) { + if ( idx+1 < argc-1 ) { + quietTime = atoi(argv[idx+1]); + } else { + pm_error("Invalid -quiet option"); + } + + idx += 2; + } else if ( strcmp(argv[idx], "-realquiet") == 0 ) { + realQuiet = TRUE; + idx++; + } else if (( strcmp(argv[idx], "-float_dct") == 0 ) || + ( strcmp(argv[idx], "-float-dct") == 0 )) { + pureDCT = TRUE; + init_idctref(); + init_fdct(); + idx++; + } else if ( strcmp(argv[idx], "-no_frame_summary") == 0 ) { + if ( idx < argc-1 ) { + noFrameSummaryOption = TRUE; + } else { + pm_error("Invalid -no_frame_summary option"); + } + + idx++; + } else if ( strcmp(argv[idx], "-snr") == 0 ) { + printSNR = TRUE; + idx++; + } else if ( strcmp(argv[idx], "-mse") == 0 ) { + printSNR = printMSE = TRUE; + idx++; + } else if ( strcmp(argv[idx], "-debug_sockets") == 0 ) { + debugSockets = TRUE; + idx++; + } else if ( strcmp(argv[idx], "-debug_machines") == 0 ) { + debugMachines = TRUE; + idx++; + } else if ( strcmp(argv[idx], "-bit_rate_info") == 0 ) { + if ( idx+1 < argc-1 ) { + bitRateInfoOption = TRUE; + SetBitRateFileName(argv[idx+1]); + idx += 2; + } else { + pm_error("Invalid -bit_rate_info option"); + } + } else if ( strcmp(argv[idx], "-mv_histogram") == 0 ) { + computeMVHist = TRUE; + idx++; + } else { + pm_error("Unrecognized option: '%s'", argv[idx]); + } + } + + cmdlineP->paramFileName = argv[argc-1]; +} + + + +static void +compileTests() { + + /* There was code here (assert() calls) that verified that uint16 + is 16 bits, etc. It caused compiler warnings that said, "Of + course it is!" (actually, "statement has no effect"). There + isn't enough chance that uint16, etc. are defined incorrectly + to warrant those asserts. 2000.05.07 + */ + + if ( (-8 >> 3) != -1 ) { + fprintf(stderr, "ERROR: Right shifts are NOT arithmetic!!!\n"); + fprintf(stderr, "Change >> to multiplies by powers of 2\n"); + exit(1); + } +} + + + +static void +announceJob(enum frameContext const context, + bool const childProcess, + unsigned int const frameStart, + unsigned int const frameEnd, + const char * const outputFileName) { + + if (!realQuiet) { + const char * outputDest; + const char * combineDest; + + if (context == CONTEXT_JUSTFRAMES) + asprintfN(&outputDest, "to individual frame files"); + else + asprintfN(&outputDest, "to file '%s'", outputFileName); + + if (childProcess) + combineDest = strdup("for delivery to combine server"); + else + combineDest = strdup(""); + + pm_message("%s: ENCODING FRAMES %u-%u to %s %s", + hostname, frameStart, frameEnd, outputDest, combineDest); + + strfree(combineDest); + strfree(outputDest); + } +} + + + +static void +encodeSomeFrames(struct inputSource * const inputSourceP, + boolean const childProcess, + enum frameContext const context, + unsigned int const frameStart, + unsigned int const frameEnd, + int32 const qtable[], + int32 const niqtable[], + FILE * const ofp, + const char * const outputFileName, + bool const wantVbvUnderflowWarning, + bool const wantVbvOverflowWarning, + boolean const printStats, + unsigned int * const encodeTimeP) { + + time_t framesTimeStart, framesTimeEnd; + unsigned int inputFrameBits; + unsigned int totalBits; + + announceJob(context, childProcess, frameStart, frameEnd, outputFileName); + + time(&framesTimeStart); + if (printStats) + PrintStartStats(framesTimeStart, context == CONTEXT_JUSTFRAMES, + frameStart, frameEnd, inputSourceP); + + GenMPEGStream(inputSourceP, context, frameStart, frameEnd, + qtable, niqtable, childProcess, ofp, outputFileName, + wantVbvUnderflowWarning, wantVbvOverflowWarning, + &inputFrameBits, &totalBits); + + time(&framesTimeEnd); + + *encodeTimeP = (unsigned int)(framesTimeEnd - framesTimeStart); + + if (!realQuiet) + pm_message("%s: COMPLETED FRAMES %u-%u (%u seconds)", + hostname, frameStart, frameEnd, *encodeTimeP); + + if (printStats) + PrintEndStats(framesTimeStart, framesTimeEnd, + inputFrameBits, totalBits); +} + + + + +static void +encodeFrames(struct inputSource * const inputSourceP, + boolean const childProcess, + const char * const masterHostname, + int const masterPortNumber, + int const whichGOP, + bool const specificFrames, + unsigned int const whichFrameStart, + unsigned int const whichFrameEnd, + int32 const qtable[], + int32 const niqtable[], + FILE * const ofp, + const char * const outputFileName, + bool const wantVbvUnderflowWarning, + bool const wantVbvOverflowWarning) { +/*---------------------------------------------------------------------------- + Encode the stream. If 'specificFrames' is true, then encode frames + 'whichFrameStart' through 'whichFrameEnd' individually. Otherwise, + encode the entire input stream as a complete MPEG stream. + + 'childProcess' means to do it as a child process that is under the + supervision of a master process and is possibly doing only part of + a larger batch. + + (If we had proper modularity, we wouldn't care, but parallel operation + was glued on to this program after it was complete). + + One thing we don't do when running as a child process is print + statistics; our master will do that for the whole job. +----------------------------------------------------------------------------*/ + unsigned int frameStart, frameEnd; + enum frameContext context; + unsigned int lastEncodeTime; + /* How long it took the encoder to do the last set of frames */ + boolean printStats; + /* We want the encoder to print start & end stats */ + + if (whichGOP != -1) { + /* He wants just one particular GOP from the middle of the movie. */ + ComputeGOPFrames(whichGOP, &frameStart, &frameEnd, + inputSourceP->numInputFiles); + context = CONTEXT_GOP; + } else if (specificFrames) { + /* He wants some pure frames from the middle of the movie */ + if (whichFrameStart > whichFrameEnd) + pm_error("You specified a starting frame number (%d) that is " + "greater than the ending frame number (%d) " + "you specified.", whichFrameStart, whichFrameEnd); + if (whichFrameEnd + 1 > inputSourceP->numInputFiles) + pm_error("You specified ending frame number %d, which is " + "beyond the number of input files you supplied (%u)", + whichFrameEnd, inputSourceP->numInputFiles); + + frameStart = whichFrameStart; + frameEnd = whichFrameEnd; + context = CONTEXT_JUSTFRAMES; + } else { + /* He wants the whole movie */ + frameStart = 0; + frameEnd = inputSourceP->numInputFiles - 1; + context = CONTEXT_WHOLESTREAM; + } + + printStats = !childProcess; + + encodeSomeFrames(inputSourceP, childProcess, context, frameStart, frameEnd, + customQtable, customNIQtable, + ofp, outputFileName, + wantVbvUnderflowWarning, wantVbvOverflowWarning, + printStats, + &lastEncodeTime); + + if (childProcess) { + boolean moreWorkToDo; + + /* A child is not capable of generating GOP or stream headers */ + assert(context == CONTEXT_JUSTFRAMES); + + moreWorkToDo = TRUE; /* initial assumption */ + while (moreWorkToDo) { + int nextFrameStart, nextFrameEnd; + + NotifyMasterDone(masterHostname, masterPortNumber, machineNumber, + lastEncodeTime, &moreWorkToDo, + &nextFrameStart, &nextFrameEnd); + if (moreWorkToDo) + encodeSomeFrames(inputSourceP, childProcess, + CONTEXT_JUSTFRAMES, + nextFrameStart, nextFrameEnd, + customQtable, customNIQtable, + NULL, outputFileName, + wantVbvUnderflowWarning, + wantVbvOverflowWarning, + FALSE, + &lastEncodeTime); + } + if (!realQuiet) + pm_message("%s: Child exiting. Master has no more work.", + hostname); + } +} + + + + +static void +runMaster(struct inputSource * const inputSourceP, + const char * const paramFileName, + const char * const outputFileName) { + + if (paramFileName[0] != '/' && paramFileName[0] != '~') + pm_error("For parallel mode, you must " + "use an absolute path for parameter file. " + "You specified '%s'", paramFileName); + else + MasterServer(inputSourceP, paramFileName, outputFileName); +} + + + +static void +getUserFrameFile(void * const handle, + unsigned int const frameNumber, + FILE ** const ifPP) { + + struct inputSource * const inputSourceP = (struct inputSource *) handle; + + if (inputSourceP->stdinUsed) + pm_error("You cannot combine frames from Standard Input."); + /* Because Caller detects end of frames by EOF */ + + if (frameNumber >= inputSourceP->numInputFiles) + *ifPP = NULL; + else { + const char * fileName; + const char * inputFileName; + + GetNthInputFileName(inputSourceP, frameNumber, &inputFileName); + + asprintfN(&fileName, "%s/%s", currentFramePath, inputFileName); + + *ifPP = fopen(fileName, "rb"); + if (*ifPP == NULL) + pm_error("Unable to open file '%s'. Errno = %d (%s)", + fileName, errno, strerror(errno)); + + strfree(inputFileName); + strfree(fileName); + } +} + + + +static void +nullDisposeFile(void * const handle, + unsigned int const frameNumber) { + + +} + + + +static unsigned int +framePoolSize(bool const sequentialInput) { +/*---------------------------------------------------------------------------- + Return the number of frames that our frame memory pool needs to have. + + 'sequentialInput' means we'll be reading from input that comes in frame + number order and we can't go back, so we'll have to have a bigger buffer + of frames than otherwise. +-----------------------------------------------------------------------------*/ + unsigned int numOfFrames; + + numOfFrames = 0; /* initial value */ + + if (sequentialInput) { + unsigned int idx; + unsigned int bcount; + + for ( idx = 0, bcount = 0; idx < strlen(framePattern); idx++) { + + /* counts the maximum number of B frames between two reference + * frames. + */ + + switch( framePattern[idx] ) { + case 'b': + bcount++; + break; + case 'i': + case 'p': + if (bcount > numOfFrames) + numOfFrames = bcount; + bcount = 0; + break; + } + + /* add 2 to hold the forward and past reference frames in addition + * to the maximum number of B's + */ + } + + numOfFrames += 2; + + } else { + /* non-interactive, only 3 frames needed */ + numOfFrames = 3; + } + return numOfFrames; +} + + + +int +main(int argc, char **argv) { + FILE *ofP; + time_t initTimeStart; + struct cmdlineInfo cmdline; + struct params params; + + ppm_init(&argc, argv); + + strcpy(encoder_name, argv[0]); + + compileTests(); + + time(&initTimeStart); + + SetStatFileName(""); + + hostname = GetHostName(); + + parseArgs(argc, argv, &cmdline); + + ReadParamFile(cmdline.paramFileName, cmdline.function, ¶ms); + + /* Jim Boucher's stuff: + if we are using a movie format then break up into frames + */ + if ((!cmdline.childProcess) && (baseFormat == JMOVIE_FILE_TYPE)) + JM2JPEG(params.inputSourceP); + + if (printSNR || (referenceFrame == DECODED_FRAME)) + decodeRefFrames = TRUE; + + showBitRatePerFrame = (bitRateInfoOption && !cmdline.childProcess); + frameSummary = (!noFrameSummaryOption && !cmdline.childProcess); + + numMachines = min(numMachines, cmdline.maxMachines); + + Tune_Init(); + Frame_Init(framePoolSize(params.inputSourceP->stdinUsed)); + + if (specificsOn) + Specifics_Init(); + + ComputeFrameTable(params.inputSourceP->stdinUsed ? + 0 : params.inputSourceP->numInputFiles); + + if (ioServer) { + IoServer(params.inputSourceP, cmdline.masterHostname, + cmdline.masterPortNumber); + return 0; + } else if (outputServer) { + CombineServer(cmdline.outputFrames, + cmdline.masterHostname, cmdline.masterPortNumber, + outputFileName); + } else if (decodeServer) { + DecodeServer(cmdline.outputFrames, outputFileName, + cmdline.masterHostname, cmdline.masterPortNumber); + } else { + if ((!cmdline.specificFrames) && + ((numMachines == 0) || (cmdline.function != ENCODE_FRAMES)) ) { + ofP = fopen(outputFileName, "wb"); + if (ofP == NULL) + pm_error("Could not open output file!"); + } else + ofP = NULL; + + if (cmdline.function == ENCODE_FRAMES) { + if ((numMachines == 0) || (cmdline.specificFrames)) { + encodeFrames(params.inputSourceP, + cmdline.childProcess, + cmdline.masterHostname, cmdline.masterPortNumber, + whichGOP, cmdline.specificFrames, + cmdline.frameStart, cmdline.frameEnd, + customQtable, customNIQtable, + ofP, outputFileName, + params.warnUnderflow, params.warnOverflow); + + } else + runMaster(params.inputSourceP, + cmdline.paramFileName, outputFileName); + } else if (cmdline.function == COMBINE_GOPS) + GOPsToMPEG(params.inputSourceP, outputFileName, ofP); + else if (cmdline.function == COMBINE_FRAMES) + FramesToMPEG(ofP, params.inputSourceP, + &getUserFrameFile, &nullDisposeFile); + } + Frame_Exit(); + + strfree(hostname); + + return 0; +} |