Annotation of generator/main/avi.c, revision 1.1.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.