/*------------------------------------------------------------------------
*
* 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 simple example demonstrating how to encode a short sequence
* of PPM files into I and P frames with: 1 Sequence, 2 GOPs, 4 Picture
* frames per GOP, 1 Slice per frame, 1 quantization scale. The P frames
* are encoded using the original I frames. The motion vector search is
* done with full pel units. The frame pattern is IPIPIP...
*
* Note: the input files should be named <prefix>000.ppm, <prefix>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 BUFFER_SIZE 50000 /* large enough to hold 2 frames and some headers */
#define FRAMES_PER_SECOND 30
#define FORWARD_F_CODE 3
#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()
{
pictures++;
if ( pictures == FRAMES_PER_SECOND ) {
pictures = 0;
seconds++;
if ( seconds == 60 ) {
seconds = 0;
minutes++;
if ( minutes == 60 ) {
minutes = 0;
hours++;
}
}
}
}
/*
*------------------------------------------------------------------------
* Swap two ByteImage pointers
*------------------------------------------------------------------------
*/
void Swap(a, b)
ByteImage **a, **b;
{
ByteImage *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, pictures);\
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);\
}
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 *qScale;
ScImage *scY, *scU, *scV;
VectorImage *fmv; /* forward motion vector */
PnmHdr *pnmHdr = PnmHdrNew();
MpegSeqHdr *seqHdr = MpegSeqHdrNew();
MpegGopHdr *gopHdr = MpegGopHdrNew();
MpegPicHdr *picHdr = MpegPicHdrNew();
int w = WIDTH, h = HEIGHT;
int halfw = w/2;
int halfh = h/2;
int mbw = (w + 15) / 16;
int mbh = (h + 15) / 16;
int sliceInfo[] = {1000}; /* this should be enough for one frame */
int sliceInfoLen = 1;
int gop, pic, temporalRef;
r = ByteNew(w, h);
g = ByteNew(w, h);
b = ByteNew(w, h);
y = ByteNew(w, h);
prevY = ByteNew(w, h);
u = ByteNew(halfw, halfh);
prevU = ByteNew(halfw, halfh);
v = ByteNew(halfw, halfh);
prevV = 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);
if (argc < 2) {
fprintf(stderr, "Not enough parameters : %s <input file prefix> <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, w);
MpegSeqHdrSetHeight(seqHdr, h);
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);
/* we are not going to change these */
MpegGopHdrSetDropFrameFlag(gopHdr, 0);
MpegGopHdrSetClosedGop(gopHdr, 0);
MpegGopHdrSetBrokenLink(gopHdr, 0);
MpegPicHdrSetVBVDelay(picHdr, 0);
MpegPicHdrSetFullPelBackward(picHdr, 0);
MpegPicHdrSetBackwardFCode(picHdr, 0);
for (gop = 0; gop < 2; gop++) {
MpegGopHdrSetHours(gopHdr, hours);
MpegGopHdrSetMinutes(gopHdr, minutes);
MpegGopHdrSetSeconds(gopHdr, seconds);
MpegGopHdrSetPictures(gopHdr, pictures);
MpegGopHdrEncode(gopHdr, obp);
temporalRef = 0;
for (pic = 0; pic < 2; pic++) {
/* I FRAME */
READ_TO_YUV(bp, bs, r, g, b, y, u, v);
MpegPicHdrSetTemporalRef(picHdr, temporalRef);
MpegPicHdrSetType(picHdr, I_FRAME);
MpegPicHdrSetFullPelForward(picHdr, 0);
MpegPicHdrSetForwardFCode(picHdr, 0);
MpegPicHdrEncode (picHdr, obp);
ByteToScI(y, qScale, MPEG_INTRA, scY); /* MPEG_INTRA is the default quantizer table */
ByteToScI(u, qScale, MPEG_INTRA, scU);
ByteToScI(v, qScale, MPEG_INTRA, scV);
MpegPicIEncode (picHdr, scY, scU, scV, qScale, sliceInfo, sliceInfoLen, obp);
IncrementTime();
temporalRef++;
Swap(&y, &prevY);
Swap(&u, &prevU);
Swap(&v, &prevV);
/* P FRAME */
READ_TO_YUV(bp, bs, r, g, b, y, u, v);
MpegPicHdrSetTemporalRef(picHdr, temporalRef);
MpegPicHdrSetType(picHdr, P_FRAME);
MpegPicHdrSetFullPelForward(picHdr, 1);
MpegPicHdrSetForwardFCode(picHdr, FORWARD_F_CODE);
MpegPicHdrEncode (picHdr, obp);
BytePMotionVecSearch(picHdr, y, prevY, NULL, 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);
MpegPicPEncode (picHdr, scY, scU, scV, fmv, qScale, sliceInfo, sliceInfoLen, obp);
IncrementTime();
temporalRef++;
/* write two frames to the bitstream */
printf("Writing to file...\n");
BitStreamFileWriteSegment(obs, outFile, 0, BitParserTell(obp));
BitParserWrap(obp, obs);
}
}
/* done forget this */
MpegSeqEndCodeEncode(obp);
BitStreamFileWriteSegment(obs, outFile, 0, BitParserTell(obp));
/* lots of things to free up */
BitStreamFree(bs);
BitParserFree(bp);
BitStreamFree(obs);
BitParserFree(obp);
MpegSeqHdrFree(seqHdr);
MpegGopHdrFree(gopHdr);
MpegPicHdrFree(picHdr);
PnmHdrFree(pnmHdr);
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);
return 0;
}