|
|
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:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.