|
|
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.