/*------------------------------------------------------------------------ * * 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. * * mpgselect.c * * Dan Rabinovitz dsr@cs.cornell.edu * * Usage : mpgselect inputMPG startFrame endFrame * * Make two pass on the MPEG file, first pass create an index, second pass * uses the index to decode gray scale version of the selected frames * (startFrame .. endFrame) from the MPEG. * *------------------------------------------------------------------------ */ #include "dvmbasic.h" #include "dvmpnm.h" #include "dvmmpeg.h" #include "dvmcolor.h" void SwapInt (x, y) int *x; int *y; { int temp; temp = *x; *x = *y; *y = temp; } void SwapByteImage (x, y) ByteImage **x; ByteImage **y; { ByteImage *temp; temp = *x; *x = *y; *y = temp; } MpegVideoIndex * MakeMpegVideoIndex (bp) BitParser *bp; { int size = 100; int firstPicInGop = 0; int picCount = 0; int done = 0; int past = 0; int future = 0; int code, offset, tempref, len, frameNum; char type; MpegVideoIndex *index = MpegVideoIndexNew (size); MpegPicHdr *ph = MpegPicHdrNew (); while (!done) { MpegAnyHdrFind (bp); code = MpegGetCurrStartCode (bp); switch (code) { case SEQ_START_CODE: /* Not initerested in Sequence Header, skip */ MpegSeqHdrSkip (bp); break; case GOP_START_CODE: /* We reach a new GOP. We record the frame * number at the start of the GOP */ firstPicInGop = picCount; MpegGopHdrSkip (bp); break; case PIC_START_CODE: offset = BitParserTell (bp); MpegPicHdrParse (bp, ph); type = MpegPicHdrGetType (ph); tempref = MpegPicHdrGetTemporalRef (ph); len = MpegAnyHdrFind (bp); frameNum = tempref + firstPicInGop; if (frameNum > size) { size <<= 1; MpegVideoIndexResize (index, size); } if (type == I_FRAME) { SwapInt (&past, &future); MpegVideoIndexTableAdd (index, frameNum, offset, type, len, 0, 0); SwapInt (&frameNum, &future); } else if (type == P_FRAME) { SwapInt (&past, &future); MpegVideoIndexTableAdd (index, frameNum, offset, type, len, past - frameNum, 0); SwapInt (&frameNum, &future); } else { MpegVideoIndexTableAdd (index, frameNum, offset, type, len, past - frameNum, future - frameNum); } picCount++; break; case SEQ_END_CODE: done = 1; break; default: done = 1; } } BitParserSeek (bp, 0); return index; } /* * This procedure encode 3 byte image into a bitstream bs, * using bitparser bp, and output it to a tcl channel called * name. Assumes that the header is already encoded in the * bitstream. (This is an improvement over the routines in pnmlib.tcl * since it reuse the same header and bitstream) */ void WritePGM (y, bs, bp, name) ByteImage *y; BitStream *bs; BitParser *bp; char *name; { FILE *chan; int curr; chan = fopen(name, "wb"); if (chan == NULL) { fprintf(stderr, "unable to open %s for reading.\n", name); exit(1); } curr = BitParserTell(bp); PgmEncode(y, bp); BitStreamFileWrite(bs, chan, 0); BitParserSeek(bp, curr); fclose(chan); } int main(int argc, char *argv[]) { BitParser *bp; BitStream *bs; BitParser *outbp; BitStream *outbs; MpegSeqHdr *sh; MpegPicHdr *fh; ByteImage *y; ByteImage *prevy, *futurey; ScImage *scy, *scu, *scv; VectorImage *fwdmv, *bwdmv; PnmHdr *pnmhdr; int len, start, end, offset, currFrame, futureFrame; int halfw, halfh, i, w, h, seqw, seqh, picSize, remw, remh, type, size; char outname[100]; MpegVideoIndex *index, *tempIndex; /* * Check arguments, open file, and initialize BitStream. */ if (argc < 3) { fprintf(stderr, "usage : %s input startFrame endFrame\n", argv[0]); exit(1); } bs = BitStreamMmapReadNew(argv[1]); if (bs == NULL) { fprintf(stderr, "unable to open %s for reading.\n", argv[1]); exit(1); } start = atoi(argv[2]); end = atoi(argv[3]); bp = BitParserNew(); BitParserWrap(bp, bs); /* * Create the Video Index. */ index = MakeMpegVideoIndex(bp); /* * Allocate a new sequence header, skips the initial garbage (if any) * in the input file, and read in the sequence header. */ sh = MpegSeqHdrNew(); MpegSeqHdrFind(bp); MpegSeqHdrParse(bp, sh); /* * Find the width and height of the video frames. If the width * and height are not multiple of 16, round it up to the next * multiple of 16. */ seqw = MpegSeqHdrGetWidth(sh); seqh = MpegSeqHdrGetHeight(sh); picSize = MpegSeqHdrGetBufferSize(sh); remw = seqw % 16; remh = seqh % 16; if (remw != 0) { w = seqw + 16 - remw; } else { w = seqw; } if (remh != 0) { h = seqh + 16 - remh; } else { h = seqh; } halfw = w/2; halfh = h/2; /* * Allocates all the ByteImages and ScImages that we need. * y, u, v for decoded frame in YUV color space, r, g, b for * decoded frame in RGB color space. prevy, prevu, prevv are * past frames in YUV color space, futurey, futureu and futurev * are future frames in YUV color space. scy, scu and scv * are the DCT coded images from the bitstream. fwdmv and bwdmv * are the forward and backward motion vectors respectively. */ y = ByteNew (w, h); prevy = ByteNew (w, h); futurey = ByteNew (w, h); fwdmv = VectorNew (w/16, h/16); bwdmv = VectorNew (w/16, h/16); scy = ScNew (w/8, h/8); scu = ScNew (w/16, h/16); scv = ScNew (w/16, h/16); /* * Create a new PnmHdr and encode it to the BitStream. We only do * this once, since all frames have the same header. */ pnmhdr = PnmHdrNew(); PnmHdrSetType(pnmhdr, PGM_BIN); PnmHdrSetWidth(pnmhdr, seqw); PnmHdrSetHeight(pnmhdr, seqh); PnmHdrSetMaxVal(pnmhdr, 255); outbs = BitStreamNew(seqw*seqh + 20); outbp = BitParserNew(); BitParserWrap(outbp, outbs); PnmHdrEncode(pnmhdr, outbp); PnmHdrFree(pnmhdr); /* * Find out how many frames must we decode in order to decode * frame $start. We then call mpeg_video_index_findrefs * to retrives index entries of frames that are needed to be * decoded in order to decode frame $start. These index * entries will be stored in a second mpeg_video_index called $out. */ fh = MpegPicHdrNew(); size = MpegVideoIndexNumRefs(index, start); tempIndex = MpegVideoIndexNew(size); MpegVideoIndexFindRefs(index, tempIndex, start); for (i = size-1; i >= 0; i--) { offset = MpegVideoIndexGetOffset(tempIndex, i); BitParserSeek(bp, offset); MpegPicHdrParse(bp, fh); type = MpegPicHdrGetType(fh); SwapByteImage(&futurey, &prevy); if (type == I_FRAME) { MpegPicIParse(bp, sh, fh, scy, scu, scv); ScIToByte(scy, y); } else if (type == P_FRAME) { MpegPicPParse(bp, sh, fh, scy, scu, scv, fwdmv); ScPToY(scy, fwdmv, prevy, y); } SwapByteImage(&y, &futurey); } SwapByteImage(&prevy, &futurey); MpegVideoIndexFree(tempIndex); futureFrame = -1; len = MpegPicHdrFind(bp); currFrame = start; while (currFrame < end) { offset = MpegVideoIndexGetOffset(index, currFrame); BitParserSeek(bp, offset); MpegPicHdrParse(bp, fh); type = MpegPicHdrGetType(fh); if (type == I_FRAME) { if (currFrame == futureFrame) { sprintf(outname, "%03di.pgm", currFrame); WritePGM(y, outbs, outbp, outname); SwapByteImage(&futurey, &prevy); } else { MpegPicIParse(bp, sh, fh, scy, scu, scv); ScIToByte(scy, y); sprintf(outname, "%03di.pgm", currFrame); WritePGM(y, outbs, outbp, outname); SwapByteImage(&y, &prevy); } } else if (type == P_FRAME) { if (currFrame == futureFrame) { sprintf(outname, "%03dp.pgm", currFrame); WritePGM(y, outbs, outbp, outname); SwapByteImage(&futurey, &prevy); } else { MpegPicPParse(bp, sh, fh, scy, scu, scv, fwdmv); ScPToY(scy, fwdmv, prevy, y); sprintf(outname, "%03dp.pgm", currFrame); WritePGM(y, outbs, outbp, outname); SwapByteImage(&y, &prevy); } } else { futureFrame = currFrame + MpegVideoIndexGetNext(index, currFrame); offset = MpegVideoIndexGetOffset(index, futureFrame); BitParserSeek(bp, offset); MpegPicHdrParse(bp, fh); type = MpegPicHdrGetType(fh); /* * Decode the future frame. */ if (type == I_FRAME) { MpegPicIParse(bp, sh, fh, scy, scu, scv); ScIToByte(scy, futurey); } else if (type == P_FRAME) { MpegPicPParse(bp, sh, fh, scy, scu, scv, fwdmv); ScPToY(scy, fwdmv, prevy, futurey); } offset = MpegVideoIndexGetOffset(index, currFrame); BitParserSeek(bp, offset); MpegPicHdrParse(bp, fh); MpegPicBParse(bp, sh, fh, scy, scu, scv, fwdmv, bwdmv); ScBToY(scy, fwdmv, bwdmv, prevy, futurey, y); sprintf(outname, "%03db.pgm", currFrame); WritePGM(y, outbs, outbp, outname); } currFrame++; } /* * Clean up the stuffs. */ MpegPicHdrFree(fh); MpegSeqHdrFree(sh); BitStreamMmapReadFree(bs); BitParserFree(bp); ByteFree(y); ByteFree(prevy); ByteFree(futurey); ScFree(scy); VectorFree(fwdmv); VectorFree(bwdmv); MpegVideoIndexFree(index); return 0; }