/*------------------------------------------------------------------------ * * 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. * * mpgtoppm.c * * Wei Tsang Ooi weitsang@cs.cornell.edu * * Usage : mpgtoppm inputMPG * * Decodes an MPEG video into a series of PPM files. * *------------------------------------------------------------------------ */ #include <sys/types.h> #include <sys/stat.h> #include "dvmbasic.h" #include "dvmmpeg.h" #include "dvmcolor.h" #include "dvmpnm.h" /* * 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 WritePPM (r, g, b, bs, bp, name) ByteImage *r; ByteImage *g; ByteImage *b; BitStream *bs; BitParser *bp; char *name; { FILE *chan; int curr; chan = fopen(name, "w"); if (chan == NULL) { fprintf(stderr, "unable to open %s for reading.\n", name); exit(1); } curr = BitParserTell(bp); PpmEncode(r, g, b, bp); BitStreamFileWrite(bs, chan, 0); BitParserSeek(bp, curr); fclose(chan); } void swap (ByteImage **x, ByteImage **y) { ByteImage *temp; temp = *x; *x = *y; *y = temp; } /* * This proc make sure that there are at least size bytes of * data in the bitstream bs, which is attached to bitparser bp. * If there is not enough data, fill up the bitstream by reading * from tcl channel chan. */ void CheckBitStreamUnderflow (bs, bp, chan, size) BitStream *bs; BitParser *bp; FILE *chan; int size; { int off = BitParserTell(bp); int left = BitStreamBytesLeft(bs, off); if (left < size) { BitStreamShift (bs, off); BitStreamFileRead (bs, chan, left); BitParserSeek (bp, 0); } } int main(int argc, char *argv[]) { BitParser *bp = BitParserNew(); BitStream *bs = BitStreamNew(65536); BitParser *outbp; BitStream *outbs; MpegSeqHdr *sh; MpegPicHdr *fh; ByteImage *r, *g, *b, *y, *u, *v; ByteImage *prevy, *prevu, *prevv, *futurey, *futureu, *futurev; ScImage *scy, *scu, *scv; VectorImage *fwdmv, *bwdmv; PnmHdr *pnmhdr; FILE *file; int currCode, len; int halfw, halfh, w, h, seqw, seqh, picSize, remw, remh, type; int counter, gopSize = 0, gopStart = 0; char outname[100]; /* * Check arguments, open file, and initialize BitStream. */ if (argc < 1) { fprintf(stderr, "usage : %s input\n", argv[0]); exit(1); } file = fopen(argv[1], "rb"); if (file == NULL) { fprintf(stderr, "unable to open %s for reading.\n", argv[1]); exit(1); } BitStreamFileRead(bs, file, 0); BitParserWrap(bp, bs); /* * 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); r = ByteNew (seqw, seqh); g = ByteNew (seqw, seqh); b = ByteNew (seqw, seqh); u = ByteNew (halfw, halfh); prevu = ByteNew (halfw, halfh); futureu = ByteNew (halfw, halfh); v = ByteNew (halfw, halfh); prevv = ByteNew (halfw, halfh); futurev = ByteNew (halfw, halfh); 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, PPM_BIN); PnmHdrSetWidth(pnmhdr, seqw); PnmHdrSetHeight(pnmhdr, seqh); PnmHdrSetMaxVal(pnmhdr, 255); outbs = BitStreamNew(3*seqw*seqh + 20); outbp = BitParserNew(); BitParserWrap(outbp, outbs); PnmHdrEncode(pnmhdr, outbp); PnmHdrFree(pnmhdr); /* * Create a new Pic header, and advance the cursor to the next * pic header. (We are not interested in GOP header here). */ fh = MpegPicHdrNew(); len = MpegPicHdrFind(bp); while (1) { /* * Reads the pic header, and perform decoding according to the * type. */ CheckBitStreamUnderflow(bs, bp, file, picSize); MpegPicHdrParse(bp, fh); type = MpegPicHdrGetType(fh); counter = gopStart + MpegPicHdrGetTemporalRef(fh); gopSize++; if (type == I_FRAME) { swap(&futurey, &prevy); swap(&futureu, &prevu); swap(&futurev, &prevv); MpegPicIParse(bp, sh, fh, scy, scu, scv); ScIToByte(scy, y); ScIToByte(scu, u); ScIToByte(scv, v); YuvToRgb420(y, u, v, r, g, b); sprintf(outname, "%03di.ppm", counter); WritePPM(r, g, b, outbs, outbp, outname); swap(&y, &futurey); swap(&u, &futureu); swap(&v, &futurev); } else if (type == P_FRAME) { swap(&futurey, &prevy); swap(&futureu, &prevu); swap(&futurev, &prevv); MpegPicPParse(bp, sh, fh, scy, scu, scv, fwdmv); ScPToY(scy, fwdmv, prevy, y); ScPToUV(scu, fwdmv, prevu, u); ScPToUV(scv, fwdmv, prevv, v); YuvToRgb420(y, u, v, r, g, b); sprintf(outname, "%03dp.ppm", counter); WritePPM(r, g, b, outbs, outbp, outname); swap(&y, &futurey); swap(&u, &futureu); swap(&v, &futurev); } else { MpegPicBParse(bp, sh, fh, scy, scu, scv, fwdmv, bwdmv); ScBToY(scy, fwdmv, bwdmv, prevy, futurey, y); ScBToUV(scu, fwdmv, bwdmv, prevu, futureu, u); ScBToUV(scv, fwdmv, bwdmv, prevv, futurev, v); YuvToRgb420(y, u, v, r, g, b); sprintf(outname, "%03db.ppm", counter); WritePPM(r, g, b, outbs, outbp, outname); } currCode = MpegGetCurrStartCode(bp); if (currCode == GOP_START_CODE) { gopStart += gopSize; gopSize = 0; } MpegPicHdrFind(bp); if (currCode == SEQ_END_CODE) { break; } } /* * Clean up the stuffs. */ MpegPicHdrFree(fh); MpegSeqHdrFree(sh); BitStreamFree(bs); BitParserFree(bp); ByteFree(r); ByteFree(g); ByteFree(b); ByteFree(y); ByteFree(u); ByteFree(v); ByteFree(prevy); ByteFree(prevu); ByteFree(prevv); ByteFree(futurey); ByteFree(futureu); ByteFree(futurev); ScFree(scy); ScFree(scu); ScFree(scv); VectorFree(fwdmv); VectorFree(bwdmv); return 0; }