/*------------------------------------------------------------------------ * * Copyright (c) 1997-1998 by Cornell University. * * See the file "license.txt" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * This is a more complicated example (than ppmtompg1.c). It demonstrates * how to encode a series of PPM files into I, P, B frames using decoded * reference frames and half-pel precision motion vector search. The * frame pattern is BIBPBIBPB... and each GOP contains 8 frames. * * Note: the input files should be named 000.ppm, 001.ppm ... *------------------------------------------------------------------------ */ #include <sys/stat.h> #include <dvmbasic.h> #include <dvmmpeg.h> #include <dvmpnm.h> #include <dvmcolor.h> #define PREFIX_SIZE 100 /* prefix to the input files including directory */ #define NUM_OF_FRAMES 20 #define GOP_SIZE 8 #define BUFFER_SIZE 100000 /* large enough to hold 4 frames and some headers */ #define FRAMES_PER_SECOND 30 #define FORWARD_F_CODE 2 #define BACKWARD_F_CODE 2 #define WIDTH 176 /* width and height of each frame in pixels */ #define HEIGHT 120 int fsize (char *name) { #ifdef __WIN32__ struct _stat s; #else struct stat s; #endif int result; result = stat(name, &s); return s.st_size; } int pictures = 0; int seconds = 0; int minutes = 0; int hours = 0; /* *------------------------------------------------------------------------ * Use this to keep track of how many picture frames, seconds, minutes, * hours. This information is encoded in the GOP headers. *------------------------------------------------------------------------ */ void IncrementTime() { if ( pictures == FRAMES_PER_SECOND ) { pictures = 0; seconds++; if ( seconds == 60 ) { seconds = 0; minutes++; if ( minutes == 60 ) { minutes = 0; hours++; } } } } /* *------------------------------------------------------------------------ * Swap two pointers *------------------------------------------------------------------------ */ void Swap(a, b) void **a, **b; { void *temp; temp = *a; *a = *b; *b = temp; } /* *------------------------------------------------------------------------ * Read in a PPM file into r, g, b ByteImages, then convert it to y, u, v * with 4:2:0 sampling *------------------------------------------------------------------------ */ #define READ_TO_YUV(bp, bs, r, g, b, y, u, v) {\ sprintf(fileName, "%s%03d.ppm", namePrefix, temporalRef + gopStart);\ printf("Processing %s\n", fileName);\ inFile = fopen(fileName, "rb");\ if (inFile == NULL) {\ fprintf(stderr, "unable to open %s for reading.\n", fileName);\ exit(1);\ }\ BitParserWrap(bp, bs);\ BitStreamFileRead(bs, inFile, 0);\ PnmHdrParse(bp, pnmHdr);\ PpmParse(bp, r, g, b);\ fclose(inFile);\ RgbToYuv420(r, g, b, y, u, v);\ } /* *------------------------------------------------------------------------ * I Frame *------------------------------------------------------------------------ */ #define I_FRAME_ENCODE {\ READ_TO_YUV(bp, bs, r, g, b, y, u, v);\ Swap(&nextY, &prevY);\ Swap(&nextU, &prevU);\ Swap(&nextV, &prevV);\ Swap(&interNext, &interPrev);\ \ ByteToScI(y, qScale, MPEG_INTRA, scY);\ ByteToScI(u, qScale, MPEG_INTRA, scU);\ ByteToScI(v, qScale, MPEG_INTRA, scV);\ \ MpegPicHdrSetTemporalRef(iPicHdr, temporalRef);\ MpegPicHdrEncode(iPicHdr, obp);\ MpegPicIEncode (iPicHdr, scY, scU, scV, qScale, sliceInfo, sliceInfoLen, obp);\ \ /* decode the image just encoded to use for encoding next P, B frame */\ ScDequantize(scY, qScale, MPEG_INTRA, scY);\ ScDequantize(scU, qScale, MPEG_INTRA, scU);\ ScDequantize(scV, qScale, MPEG_INTRA, scV);\ ScIToByte(scY, nextY);\ ScIToByte(scU, nextU);\ ScIToByte(scV, nextV);\ ByteComputeIntermediates(nextY, interNext);\ IncrementTime();\ }\ /* *------------------------------------------------------------------------ * P Frame *------------------------------------------------------------------------ */ #define P_FRAME_ENCODE {\ READ_TO_YUV(bp, bs, r, g, b, y, u, v);\ Swap(&nextY, &prevY);\ Swap(&nextU, &prevU);\ Swap(&nextV, &prevV);\ Swap(&interNext, &interPrev);\ \ BytePMotionVecSearch(pPicHdr, y, prevY, interPrev, fmv);\ ByteYToScP (y, prevY, fmv, qScale, MPEG_INTRA, MPEG_NON_INTRA, scY);\ ByteUVToScP(u, prevU, fmv, qScale, MPEG_INTRA, MPEG_NON_INTRA, scU);\ ByteUVToScP(v, prevV, fmv, qScale, MPEG_INTRA, MPEG_NON_INTRA, scV);\ \ MpegPicHdrSetTemporalRef(pPicHdr, temporalRef);\ MpegPicHdrEncode (pPicHdr, obp);\ MpegPicPEncode (pPicHdr, scY, scU, scV, fmv, qScale, sliceInfo, sliceInfoLen, obp);\ \ /* decode the image just encoded to use for encoding next B frame */\ ScNonIDequantize(scY, qScale, MPEG_INTRA, MPEG_NON_INTRA, scY);\ ScNonIDequantize(scU, qScale, MPEG_INTRA, MPEG_NON_INTRA, scU);\ ScNonIDequantize(scV, qScale, MPEG_INTRA, MPEG_NON_INTRA, scV);\ ScPToY (scY, fmv, prevY, nextY);\ ScPToUV(scU, fmv, prevU, nextU);\ ScPToUV(scV, fmv, prevV, nextV);\ ByteComputeIntermediates(nextY, interNext);\ IncrementTime();\ } /* *------------------------------------------------------------------------ * B Frame *------------------------------------------------------------------------ */ #define B_FRAME_ENCODE {\ READ_TO_YUV(bp, bs, r, g, b, y, u, v);\ ByteBMotionVecSearch(bPicHdr, y, prevY, nextY, interPrev, interNext, sliceInfo, sliceInfoLen, fmv, bmv);\ ByteYToScB (y, prevY, nextY, fmv, bmv, qScale, MPEG_INTRA, MPEG_NON_INTRA, scY);\ ByteUVToScB(u, prevU, nextU, fmv, bmv, qScale, MPEG_INTRA, MPEG_NON_INTRA, scU);\ ByteUVToScB(v, prevV, nextV, fmv, bmv, qScale, MPEG_INTRA, MPEG_NON_INTRA, scV);\ MpegPicHdrSetTemporalRef(bPicHdr, temporalRef);\ MpegPicHdrEncode (bPicHdr, obp);\ MpegPicBEncode (bPicHdr, scY, scU, scV, fmv, bmv, qScale, sliceInfo, sliceInfoLen, obp);\ IncrementTime();\ } int main(int argc, char *argv[]) { FILE *inFile, *outFile; char fileName[PREFIX_SIZE+3]; char namePrefix[PREFIX_SIZE]; BitParser *bp = BitParserNew(); /* input bitstream */ BitStream *bs; BitParser *obp = BitParserNew(); /* output bitstream */ BitStream *obs = BitStreamNew(BUFFER_SIZE); ByteImage *r, *g, *b, *y, *u, *v; ByteImage *prevY, *prevU, *prevV; ByteImage *nextY, *nextU, *nextV; ByteImage *interPrev[3], *interNext[3]; ByteImage *qScale; ScImage *scY, *scU, *scV; VectorImage *fmv, *bmv; PnmHdr *pnmHdr = PnmHdrNew(); MpegSeqHdr *seqHdr = MpegSeqHdrNew(); MpegGopHdr *gopHdr = MpegGopHdrNew(); MpegPicHdr *iPicHdr = MpegPicHdrNew(); MpegPicHdr *pPicHdr = MpegPicHdrNew(); MpegPicHdr *bPicHdr = MpegPicHdrNew(); int mbw = (WIDTH + 15) / 16; int mbh = (HEIGHT + 15) / 16; int w = mbw*16; int h = mbh*16; int halfw = w/2; int halfh = h/2; int sliceInfo[] = {1000}; /* this should be enough for one frame */ int sliceInfoLen = 1; int gop, pic; int temporalRef, gopStart = 0, currentGopSize; r = ByteNew(w, h); g = ByteNew(w, h); b = ByteNew(w, h); y = ByteNew(w, h); prevY = ByteNew(w, h); nextY = ByteNew(w, h); u = ByteNew(halfw, halfh); v = ByteNew(halfw, halfh); prevU = ByteNew(halfw, halfh); prevV = ByteNew(halfw, halfh); nextU = ByteNew(halfw, halfh); nextV = ByteNew(halfw, halfh); qScale = ByteNew(mbw, mbh); scY = ScNew(mbw*2, mbh*2); scU = ScNew(mbw, mbh); scV = ScNew(mbw, mbh); fmv = VectorNew(mbw, mbh); bmv = VectorNew(mbw, mbh); interPrev[0] = ByteNew(w-1, h); interNext[0] = ByteNew(w-1, h); interPrev[1] = ByteNew(w, h-1); interNext[1] = ByteNew(w, h-1); interPrev[2] = ByteNew(w-1, h-1); interNext[2] = ByteNew(w-1, h-1); if (argc < 2) { fprintf(stderr, "Not enough parameters : %s <input directroy> <output file name>\n", argv[0]); exit(1); } /* Get input file size */ strcpy(namePrefix, argv[1]); sprintf(fileName, "%s000.ppm", namePrefix); inFile = fopen(fileName, "rb"); if (inFile == NULL) { fprintf(stderr, "unable to open %s for reading.\n", fileName); exit(1); } bs = BitStreamNew(fsize(fileName)); fclose(inFile); /* Create outfile file */ outFile = fopen(argv[2], "wb"); if (outFile == NULL) { fprintf(stderr, "unable to open %s for writing.\n", argv[2]); exit(1); } BitParserWrap(obp, obs); ByteSet(qScale, 4); MpegSeqHdrSetWidth(seqHdr, WIDTH); MpegSeqHdrSetHeight(seqHdr, HEIGHT); MpegSeqHdrSetAspectRatio(seqHdr, 1.000); MpegSeqHdrSetPicRate(seqHdr, FRAMES_PER_SECOND); MpegSeqHdrSetBitRate(seqHdr, -1); MpegSeqHdrSetBufferSize(seqHdr, 16); MpegSeqHdrSetConstrained(seqHdr, 0); MpegSeqHdrSetDefaultIQT(seqHdr); MpegSeqHdrSetDefaultNIQT(seqHdr); MpegSeqHdrEncode(seqHdr, obp); /* GOP header */ MpegGopHdrSetDropFrameFlag(gopHdr, 0); MpegGopHdrSetClosedGop(gopHdr, 1); MpegGopHdrSetBrokenLink(gopHdr, 0); MpegGopHdrSetHours(gopHdr, hours); MpegGopHdrSetMinutes(gopHdr, minutes); MpegGopHdrSetSeconds(gopHdr, seconds); MpegGopHdrSetPictures(gopHdr, pictures); /* I Frame picHdr */ MpegPicHdrSetVBVDelay(iPicHdr, 0); MpegPicHdrSetType(iPicHdr, I_FRAME); MpegPicHdrSetFullPelForward(iPicHdr, 0); MpegPicHdrSetForwardFCode(iPicHdr, 0); MpegPicHdrSetFullPelBackward(iPicHdr, 0); MpegPicHdrSetBackwardFCode(iPicHdr, 0); /* P Frame picHdr */ MpegPicHdrSetVBVDelay(pPicHdr, 0); MpegPicHdrSetType(pPicHdr, P_FRAME); MpegPicHdrSetFullPelForward(pPicHdr, 0); MpegPicHdrSetForwardFCode(pPicHdr, FORWARD_F_CODE); MpegPicHdrSetFullPelBackward(pPicHdr, 0); MpegPicHdrSetBackwardFCode(pPicHdr, 0); /* B Frame picHdr */ MpegPicHdrSetVBVDelay(bPicHdr, 0); MpegPicHdrSetType(bPicHdr, B_FRAME); MpegPicHdrSetFullPelForward(bPicHdr, 0); MpegPicHdrSetForwardFCode(bPicHdr, 0); /* special case for 1st frame */ MpegPicHdrSetFullPelBackward(bPicHdr, 0); MpegPicHdrSetBackwardFCode(bPicHdr, BACKWARD_F_CODE); /* Encode the first GOP, which is different because of the first B frame */ MpegGopHdrEncode(gopHdr, obp); temporalRef = 1; I_FRAME_ENCODE; temporalRef += -1; B_FRAME_ENCODE; temporalRef += 3; P_FRAME_ENCODE; temporalRef += -1; MpegPicHdrSetForwardFCode(bPicHdr, FORWARD_F_CODE); /* for the rest do, bi-directional */ B_FRAME_ENCODE; /* write what we have so far to the bitstream */ printf("Writing to file...\n"); BitStreamFileWriteSegment(obs, outFile, 0, BitParserTell(obp)); BitParserWrap(obp, obs); /* Encode the remaining GOP's */ gopStart = 4; for (gop = 4; gop < NUM_OF_FRAMES; gop += GOP_SIZE) { MpegGopHdrSetHours(gopHdr, hours); MpegGopHdrSetMinutes(gopHdr, minutes); MpegGopHdrSetSeconds(gopHdr, seconds); MpegGopHdrSetPictures(gopHdr, pictures); MpegGopHdrEncode(gopHdr, obp); currentGopSize = 0; temporalRef = 1; for (pic = 0; pic < 2; pic++) { I_FRAME_ENCODE; temporalRef += -1; B_FRAME_ENCODE; temporalRef += 3; P_FRAME_ENCODE; temporalRef += -1; B_FRAME_ENCODE; temporalRef += 3; currentGopSize += 4; /* write two frames to the bitstream */ printf("Writing to file...\n"); BitStreamFileWriteSegment(obs, outFile, 0, BitParserTell(obp)); BitParserWrap(obp, obs); } gopStart += currentGopSize; } /* don't forget this */ MpegSeqEndCodeEncode(obp); BitStreamFileWriteSegment(obs, outFile, 0, BitParserTell(obp)); BitParserWrap(obp, obs); fclose(outFile); /* lots of things to free up */ BitStreamFree(bs); BitParserFree(bp); BitStreamFree(obs); BitParserFree(obp); PnmHdrFree(pnmHdr); MpegSeqHdrFree(seqHdr); MpegGopHdrFree(gopHdr); MpegPicHdrFree(iPicHdr); MpegPicHdrFree(pPicHdr); MpegPicHdrFree(bPicHdr); ByteFree(r); ByteFree(g); ByteFree(b); ByteFree(y); ByteFree(u); ByteFree(v); ByteFree(prevY); ByteFree(prevU); ByteFree(prevV); ByteFree(qScale); ScFree(scY); ScFree(scU); ScFree(scV); VectorFree(fmv); VectorFree(bmv); for (pic = 0; pic < 3; pic++) { ByteFree(interPrev[pic]); ByteFree(interNext[pic]); } return 0; }