Annotation of generator/main/avi.c, revision 1.1

1.1     ! root        1: /* Generator is (c) James Ponder, 1997-2001 http://www.squish.net/generator/ */
        !             2: 
        !             3: #include <sys/types.h>
        !             4: #include <unistd.h>
        !             5: #include <stdio.h>
        !             6: #include <stdlib.h>
        !             7: #include <errno.h>
        !             8: 
        !             9: #include "generator.h"
        !            10: 
        !            11: #include "vdp.h"
        !            12: #include "ui.h"
        !            13: #include "dib.h"
        !            14: #include "avi.h"
        !            15: 
        !            16: #ifdef JPEG
        !            17: #  include "jpeglib.h"
        !            18: #endif
        !            19: 
        !            20: /* From vfw.h */
        !            21: 
        !            22: /* The following is a short description of the AVI file format.  Please
        !            23:  * see the accompanying documentation for a full explanation.
        !            24:  *
        !            25:  * An AVI file is the following RIFF form:
        !            26:  *
        !            27:  *  RIFF('AVI' 
        !            28:  *        LIST('hdrl'
        !            29:  *        avih(<MainAVIHeader>)
        !            30:  *                  LIST ('strl'
        !            31:  *                      strh(<Stream header>)
        !            32:  *                      strf(<Stream format>)
        !            33:  *                      ... additional header data
        !            34:  *            LIST('movi'  
        !            35:  *          { LIST('rec' 
        !            36:  *                SubChunk...
        !            37:  *             )
        !            38:  *              | SubChunk } ....     
        !            39:  *            )
        !            40:  *            [ <AVIIndex> ]
        !            41:  *      )
        !            42:  *
        !            43:  *  The main file header specifies how many streams are present.  For
        !            44:  *  each one, there must be a stream header chunk and a stream format
        !            45:  *  chunk, enlosed in a 'strl' LIST chunk.  The 'strf' chunk contains
        !            46:  *  type-specific format information; for a video stream, this should
        !            47:  *  be a BITMAPINFO structure, including palette.  For an audio stream,
        !            48:  *  this should be a WAVEFORMAT (or PCMWAVEFORMAT) structure.
        !            49:  *
        !            50:  *  The actual data is contained in subchunks within the 'movi' LIST 
        !            51:  *  chunk.  The first two characters of each data chunk are the
        !            52:  *  stream number with which that data is associated.
        !            53:  *
        !            54:  *  Some defined chunk types:
        !            55:  *           Video Streams:
        !            56:  *                  ##db: RGB DIB bits
        !            57:  *                  ##dc: RLE8 compressed DIB bits
        !            58:  *                  ##pc: Palette Change
        !            59:  *
        !            60:  *           Audio Streams:
        !            61:  *                  ##wb: waveform audio bytes
        !            62:  *
        !            63:  * The grouping into LIST 'rec' chunks implies only that the contents of
        !            64:  *   the chunk should be read into memory at the same time.  This
        !            65:  *   grouping is used for files specifically intended to be played from 
        !            66:  *   CD-ROM.
        !            67:  *
        !            68:  * The index chunk at the end of the file should contain one entry for 
        !            69:  *   each data chunk in the file.
        !            70:  *       
        !            71:  * Limitations for the current software:
        !            72:  *  Only one video stream and one audio stream are allowed.
        !            73:  *  The streams must start at the beginning of the file.
        !            74:  *
        !            75:  * 
        !            76:  * To register codec types please obtain a copy of the Multimedia
        !            77:  * Developer Registration Kit from:
        !            78:  *
        !            79:  *  Microsoft Corporation
        !            80:  *  Multimedia Systems Group
        !            81:  *  Product Marketing
        !            82:  *  One Microsoft Way
        !            83:  *  Redmond, WA 98052-6399
        !            84:  *
        !            85:  */
        !            86: 
        !            87: #ifdef JPEG
        !            88: struct jpeg_compress_struct avi_cinfo;
        !            89: struct jpeg_error_mgr avi_jerr;
        !            90: #endif
        !            91: 
        !            92: t_avi *avi_open(char *filename, t_aviinfo *info, int jpeg)
        !            93: {
        !            94:   t_avi *avi;
        !            95:   int saved_errno;
        !            96:   t_avi_header h;
        !            97:   unsigned int rate;
        !            98:   uint32 usperframe;
        !            99:   t_bmih dib;
        !           100:   t_bmih *diblittle;
        !           101: 
        !           102:   memset(&h, 0, sizeof(t_avi_header));
        !           103:   if ((avi = malloc(sizeof(t_avi))) == NULL)
        !           104:     return NULL;
        !           105:   memset(avi, 0, sizeof(t_avi));
        !           106:   avi->fd = fopen(filename, "wb");
        !           107:   if (avi->fd == NULL) {
        !           108:     saved_errno = errno;
        !           109:     free(avi);
        !           110:     errno = saved_errno;
        !           111:     return NULL;
        !           112:   }
        !           113:   memcpy(&avi->info, info, sizeof(avi->info));
        !           114: 
        !           115:   /* record items that need to be post-written */
        !           116:   avi->off_length = (char *)&h.length - (char *)&h;
        !           117:   avi->off_movilength = (char *)&h.movi.length - (char *)&h;
        !           118:   avi->off_totalframes = (char *)&h.hdrl.avih.TotalFrames - (char *)&h;
        !           119:   avi->off_videolength = (char *)&h.hdrl.strl_video.strh.Length - (char *)&h;
        !           120:   avi->off_audiolength = (char *)&h.hdrl.strl_audio.strh.Length - (char *)&h;
        !           121: 
        !           122:   rate = 0;
        !           123:   rate+= info->width * info->height * 3; /* 24 bit bmp */
        !           124:   rate+= 2 * 2 * info->sampspersec * info->fps / 1000; /* stereo, 16 bit */
        !           125:   usperframe = (long long)1000000000 / info->fps;
        !           126:   memcpy(h.riff, "RIFF", 4);
        !           127:   h.length = 0; /* to be filled in later */
        !           128:   memcpy(h.avi, "AVI ", 4);
        !           129:   memcpy(h.hdrl.list, "LIST", 4);
        !           130:   h.hdrl.length = LOCENDIAN32L(sizeof(h.hdrl) - 8);
        !           131:   memcpy(h.hdrl.hdrl, "hdrl", 4);
        !           132:   memcpy(h.hdrl.avih.avih, "avih", 4);
        !           133:   h.hdrl.avih.length = LOCENDIAN32L(sizeof(h.hdrl.avih) - 8);
        !           134:   h.hdrl.avih.MicroSecPerFrame = LOCENDIAN32L(usperframe);
        !           135:   h.hdrl.avih.MaxBytesPerSec = LOCENDIAN32L(rate * info->fps / 1000);
        !           136:   h.hdrl.avih.PaddingGranularity = 0;
        !           137:   h.hdrl.avih.Flags = LOCENDIAN32L(AVIF_ISINTERLEAVED);
        !           138:   h.hdrl.avih.TotalFrames = 0; /* to be filled in later */
        !           139:   h.hdrl.avih.InitialFrames = 0;
        !           140:   h.hdrl.avih.Streams = LOCENDIAN32L(2);
        !           141:   h.hdrl.avih.SuggestedBufferSize = LOCENDIAN32L(rate * 2); /* 2 for luck */
        !           142:   h.hdrl.avih.Width = LOCENDIAN32L(info->width);
        !           143:   h.hdrl.avih.Height = LOCENDIAN32L(info->height);
        !           144: 
        !           145:   /* VIDEO strl LIST */
        !           146: 
        !           147:   memcpy(h.hdrl.strl_video.list, "LIST", 4);
        !           148:   h.hdrl.strl_video.length = LOCENDIAN32L(sizeof(h.hdrl.strl_video) - 8);
        !           149:   memcpy(h.hdrl.strl_video.strl, "strl", 4);
        !           150:   memcpy(h.hdrl.strl_video.strh.strh, "strh", 4);
        !           151:   h.hdrl.strl_video.strh.length =
        !           152:     LOCENDIAN32L(sizeof(h.hdrl.strl_video.strh) - 8);
        !           153:   memcpy(h.hdrl.strl_video.strh.Type, "vids", 4);
        !           154:   memset(&h.hdrl.strl_video.strh.Handler, 0, 4);
        !           155:   h.hdrl.strl_video.strh.Flags = LOCENDIAN32L(0);
        !           156:   h.hdrl.strl_video.strh.Priority = LOCENDIAN16L(0);
        !           157:   h.hdrl.strl_video.strh.Language = LOCENDIAN16L(0);
        !           158:   h.hdrl.strl_video.strh.InitialFrames = LOCENDIAN32L(0);
        !           159:   h.hdrl.strl_video.strh.Scale = LOCENDIAN32L(usperframe);
        !           160:   h.hdrl.strl_video.strh.Rate = LOCENDIAN32L(1000000);
        !           161:   h.hdrl.strl_video.strh.Start = LOCENDIAN32L(0);
        !           162:   h.hdrl.strl_video.strh.Length = 0; /* to be filled in later */
        !           163:   h.hdrl.strl_video.strh.SuggestedBufferSize = LOCENDIAN32L(rate * 2);
        !           164:   h.hdrl.strl_video.strh.Quality = LOCENDIAN32L(0); /* this or 10,000? */
        !           165:   h.hdrl.strl_video.strh.SampleSize = LOCENDIAN32L(0); /* n/a XXX: Check */
        !           166:   h.hdrl.strl_video.strh.rcFrame.left = LOCENDIAN32L(0);
        !           167:   h.hdrl.strl_video.strh.rcFrame.top = LOCENDIAN32L(0);
        !           168:   h.hdrl.strl_video.strh.rcFrame.right = LOCENDIAN32L(info->width);
        !           169:   h.hdrl.strl_video.strh.rcFrame.bottom = LOCENDIAN32L(info->height);
        !           170:   memcpy(h.hdrl.strl_video.strf.strf, "strf", 4);
        !           171:   h.hdrl.strl_video.strf.length =
        !           172:       LOCENDIAN32L(sizeof(h.hdrl.strl_video.strf) - 8);
        !           173:   dib_setheader(&dib, info->width, info->height);
        !           174:   diblittle = &h.hdrl.strl_video.strf.bmih;
        !           175:   diblittle->biSize = LOCENDIAN32L(dib.biSize);
        !           176:   diblittle->biWidth = LOCENDIAN32L(dib.biWidth);
        !           177:   diblittle->biHeight = LOCENDIAN32L(dib.biHeight);
        !           178:   diblittle->biPlanes = LOCENDIAN16L(dib.biPlanes);
        !           179:   diblittle->biBitCount = LOCENDIAN16L(dib.biBitCount);
        !           180:   diblittle->biCompression = LOCENDIAN32L(dib.biCompression);
        !           181:   diblittle->biSizeImage = LOCENDIAN32L(dib.biSizeImage);
        !           182:   diblittle->biXPelsPerMeter = LOCENDIAN32L(dib.biXPelsPerMeter);
        !           183:   diblittle->biYPelsPerMeter = LOCENDIAN32L(dib.biYPelsPerMeter);
        !           184:   diblittle->biClrUsed = LOCENDIAN32L(dib.biClrUsed);
        !           185:   diblittle->biClrImportant = LOCENDIAN32L(dib.biClrImportant);
        !           186: 
        !           187:   /* AUDIO strl LIST */
        !           188: 
        !           189:   memcpy(h.hdrl.strl_audio.list, "LIST", 4);
        !           190:   h.hdrl.strl_audio.length = LOCENDIAN32L(sizeof(h.hdrl.strl_audio) - 8);
        !           191:   memcpy(h.hdrl.strl_audio.strl, "strl", 4);
        !           192:   memcpy(h.hdrl.strl_audio.strh.strh, "strh", 4);
        !           193:   h.hdrl.strl_audio.strh.length =
        !           194:     LOCENDIAN32L(sizeof(h.hdrl.strl_audio.strh) - 8);
        !           195:   memcpy(h.hdrl.strl_audio.strh.Type, "auds", 4);
        !           196:   memset(&h.hdrl.strl_audio.strh.Handler, 0, 4);
        !           197:   h.hdrl.strl_audio.strh.Flags = LOCENDIAN32L(0);
        !           198:   h.hdrl.strl_audio.strh.Priority = LOCENDIAN16L(0);
        !           199:   h.hdrl.strl_audio.strh.Language = LOCENDIAN16L(0);
        !           200:   h.hdrl.strl_audio.strh.InitialFrames = LOCENDIAN32L(0);
        !           201:   h.hdrl.strl_audio.strh.Scale = LOCENDIAN32L(1);
        !           202:   h.hdrl.strl_audio.strh.Rate = LOCENDIAN32L(info->sampspersec);
        !           203:   h.hdrl.strl_audio.strh.Start = LOCENDIAN32L(0);
        !           204:   h.hdrl.strl_audio.strh.Length = 0; /* to be filled in later */
        !           205:   h.hdrl.strl_audio.strh.SuggestedBufferSize = 0; /* XXX: ok? */
        !           206:   h.hdrl.strl_audio.strh.Quality = LOCENDIAN32L(0); /* this or 10,000? */
        !           207:   h.hdrl.strl_audio.strh.SampleSize = LOCENDIAN32L(4); /* stereo 16 bit */
        !           208:   h.hdrl.strl_audio.strh.rcFrame.left = LOCENDIAN32L(0); /* n/a */
        !           209:   h.hdrl.strl_audio.strh.rcFrame.top = LOCENDIAN32L(0); /* n/a */
        !           210:   h.hdrl.strl_audio.strh.rcFrame.right = LOCENDIAN32L(0); /* n/a */
        !           211:   h.hdrl.strl_audio.strh.rcFrame.bottom = LOCENDIAN32L(0); /* n/a */
        !           212:   memcpy(h.hdrl.strl_audio.strf.strf, "strf", 4);
        !           213:   h.hdrl.strl_audio.strf.length =
        !           214:       LOCENDIAN32L(sizeof(h.hdrl.strl_audio.strf) - 8);
        !           215:   h.hdrl.strl_audio.strf.wFormatTag = LOCENDIAN16L(WAVE_FORMAT_PCM);
        !           216:   h.hdrl.strl_audio.strf.nChannels = LOCENDIAN16L(2);
        !           217:   h.hdrl.strl_audio.strf.nSamplesPerSec = LOCENDIAN32L(info->sampspersec);
        !           218:   h.hdrl.strl_audio.strf.nAvgBytesPerSec = LOCENDIAN32L(info->sampspersec * 4);
        !           219:   h.hdrl.strl_audio.strf.nBlockAlign = LOCENDIAN16L(4); /* stereo 16 bit */
        !           220:   h.hdrl.strl_audio.strf.wBitsPerSample = LOCENDIAN16L(16);
        !           221: 
        !           222:   /* trailing movi... */
        !           223: 
        !           224:   memcpy(h.movi.list, "LIST", 4);
        !           225:   h.movi.length = 0; /* to be filled in later */
        !           226:   memcpy(h.movi.movi, "movi", 4);
        !           227: 
        !           228: #ifdef JPEG
        !           229:   if (jpeg) {
        !           230:     memcpy(&h.hdrl.strl_video.strh.Handler, "MJPG", 4);
        !           231:     memcpy(&diblittle->biCompression, "MJPG", 4);
        !           232:     avi->jpeg = 1;
        !           233:     avi->bgr = 0; /* jpeg needs RGB data */
        !           234:     avi_cinfo.err = jpeg_std_error(&avi_jerr);
        !           235:     jpeg_create_compress(&avi_cinfo);
        !           236:     jpeg_stdio_dest(&avi_cinfo, avi->fd);
        !           237:     avi_cinfo.image_width = avi->info.width;
        !           238:     avi_cinfo.image_height = avi->info.height;
        !           239:     avi_cinfo.input_components = 3;
        !           240:     avi_cinfo.in_color_space = JCS_RGB;
        !           241:     jpeg_set_defaults(&avi_cinfo);
        !           242:     jpeg_set_quality(&avi_cinfo, info->jpegquality, 0);
        !           243:   } else {
        !           244: #endif
        !           245:     avi->jpeg = 0;
        !           246:     avi->bgr = 1; /* uncompressed avi needs BGR data */
        !           247: #ifdef JPEG
        !           248:   }
        !           249: #endif
        !           250: 
        !           251:   /* pre-create ##db, ##wb chunk headers */
        !           252:   sprintf(avi->dibchunkh, "00db");
        !           253:   sprintf(avi->wavechunkh, "01wb");
        !           254: 
        !           255:   avi->linebytes = ((3 * info->width) + 3) & ~3; /* round up */
        !           256:   if (fwrite(&h, sizeof(h), 1, avi->fd) != 1) {
        !           257:     free(avi);
        !           258:     fclose(avi->fd);
        !           259:     errno = EIO;
        !           260:     return NULL;
        !           261:   }
        !           262:   avi->frames = 0;
        !           263:   avi->audiolength = 0;
        !           264:   avi->movilength = 4; /* "movi" string */
        !           265:   return avi;
        !           266: }
        !           267: 
        !           268: static int avi_update32(t_avi *avi, uint32 offset, uint32 data)
        !           269: {
        !           270:   uint32 little_data;
        !           271: 
        !           272:   if (fseek(avi->fd, offset, SEEK_SET) != 0)
        !           273:     return -1;
        !           274: 
        !           275:   little_data = LOCENDIAN32L(data);
        !           276:   if (fwrite(&little_data, 4, 1, avi->fd) != 1) {
        !           277:     errno = EIO;
        !           278:     return -1;
        !           279:   }
        !           280:   return 0;
        !           281: }
        !           282: 
        !           283: int avi_close(t_avi *avi)
        !           284: {
        !           285:   int saved_errno;
        !           286:   long pos;
        !           287: 
        !           288:   if ((pos = ftell(avi->fd)) == -1)
        !           289:     return -1;
        !           290: 
        !           291:   if (avi_update32(avi, avi->off_length, pos - 8) != 0 ||
        !           292:       avi_update32(avi, avi->off_movilength, avi->movilength) != 0 ||
        !           293:       avi_update32(avi, avi->off_totalframes, avi->frames) != 0 ||
        !           294:       avi_update32(avi, avi->off_videolength, avi->frames) != 0 ||
        !           295:       avi_update32(avi, avi->off_audiolength, avi->audiolength) != 0) {
        !           296:     saved_errno = errno;
        !           297:     fclose(avi->fd);
        !           298:     errno = saved_errno;
        !           299:     return -1;
        !           300:   }
        !           301:   if (fclose(avi->fd) != 0)
        !           302:     return -1;
        !           303: #ifdef JPEG
        !           304:   jpeg_destroy_compress(&avi_cinfo);
        !           305: #endif
        !           306:   return 0;
        !           307: }
        !           308: 
        !           309: #ifdef JPEG
        !           310: 
        !           311: int avi_video_jpeg(t_avi *avi, uint8 *video)
        !           312: {
        !           313:   JSAMPROW row[1];
        !           314:   unsigned int line;
        !           315:   long pos1, pos2;
        !           316:   int pad;
        !           317:   uint32 zero = 0;
        !           318: 
        !           319:   if ((pos1 = ftell(avi->fd)) == -1)
        !           320:     return -1;
        !           321: 
        !           322:   *(uint32 *)(avi->dibchunkh + 4) = 0;
        !           323:   if (fwrite(avi->dibchunkh, 8, 1, avi->fd) != 1) {
        !           324:     errno = EIO;
        !           325:     return -1;
        !           326:   }
        !           327:   jpeg_start_compress(&avi_cinfo, TRUE);
        !           328:   for (line = 0; line < avi->info.height; line++) {
        !           329:     row[0] = video + line * (3 * avi->info.width);
        !           330:     jpeg_write_scanlines(&avi_cinfo, row, 1);
        !           331:   }
        !           332:   jpeg_finish_compress(&avi_cinfo);
        !           333: 
        !           334:   if ((pos2 = ftell(avi->fd)) == -1)
        !           335:     return -1;
        !           336: 
        !           337:   pad = ((pos2 & 3) == 0 ? 0 : (4 - (pos2 & 3)));
        !           338:   if (fwrite(&zero, pad, 1, avi->fd) != 1) {
        !           339:     errno = EIO;
        !           340:     return -1;
        !           341:   }
        !           342: 
        !           343:   /* write chunk length at pos1 + 4 */
        !           344:   avi_update32(avi, pos1 + 4, pos2 - pos1 + pad - 8);
        !           345: 
        !           346:   if (fseek(avi->fd, 0, SEEK_END) != 0)
        !           347:     return -1;
        !           348: 
        !           349:   avi->frames+= 1;
        !           350:   avi->movilength+= pos2 - pos1 + pad;
        !           351:   return 0;
        !           352: }
        !           353: 
        !           354: #endif
        !           355: 
        !           356: int avi_video_raw(t_avi *avi, uint8 *video)
        !           357: {
        !           358:   uint32 size;
        !           359:   unsigned int line, x;
        !           360:   uint8 buf[3 * 1024];
        !           361:   uint8 *p, *v;
        !           362: 
        !           363:   if (avi->linebytes > sizeof(buf))
        !           364:     ui_err("avi_video_raw buffer too small");
        !           365: 
        !           366:   size = avi->linebytes * avi->info.height;
        !           367: 
        !           368:   *(uint32 *)(avi->dibchunkh + 4) = LOCENDIAN32L(size);
        !           369:   if (fwrite(avi->dibchunkh, 8, 1, avi->fd) != 1) {
        !           370:     errno = EIO;
        !           371:     return -1;
        !           372:   }
        !           373:   /* convert from RGB to BGR */
        !           374:   for (line = 0; line < avi->info.height; line++) {
        !           375:     v = video + (avi->info.height - line - 1) * (avi->info.width * 3);
        !           376:     p = buf;
        !           377:     for (x = 0; x < avi->info.width; x++) {
        !           378:       *p++ = v[2];
        !           379:       *p++ = v[1];
        !           380:       *p++ = v[0];
        !           381:       v+= 3;
        !           382:     }
        !           383:     /* we should have avi->linebytes of data - pad with zeros */
        !           384:     for (x = 0; x < (avi->linebytes - (avi->info.width * 3)); x++)
        !           385:       *p++ = '\0'; /* pad */
        !           386:     if (fwrite(buf, avi->linebytes, 1, avi->fd) != 1) {
        !           387:       errno = EIO;
        !           388:       return -1;
        !           389:     }
        !           390:   }
        !           391:   avi->frames+= 1;
        !           392:   avi->movilength+= 8 + avi->linebytes * avi->info.height;
        !           393:   return 0;
        !           394: }
        !           395: 
        !           396: int avi_video(t_avi *avi, uint8 *video)
        !           397: {
        !           398: #ifdef JPEG
        !           399:   if (avi->jpeg)
        !           400:     return avi_video_jpeg(avi, video);
        !           401: #endif
        !           402:   return avi_video_raw(avi, video);
        !           403: }
        !           404: 
        !           405: int avi_audio(t_avi *avi, uint8 *audio, uint32 samples)
        !           406: {
        !           407:   /* audio format is left/right 16 bit little samples */
        !           408: 
        !           409:   *(uint32 *)(avi->wavechunkh + 4) = LOCENDIAN32L(4 * samples);
        !           410:   if (fwrite(avi->wavechunkh, 8, 1, avi->fd) != 1 ||
        !           411:       fwrite(audio, 4 * samples, 1, avi->fd) != 1) {
        !           412:     errno = EIO;
        !           413:     return -1;
        !           414:   }
        !           415:   avi->audiolength+= samples;
        !           416:   avi->movilength+= 8 + 4 * samples;
        !           417:   return 0;
        !           418: }
        !           419: 

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.