1 #include "config.h"
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <unistd.h>
6 #include <string.h>
7 #include <fcntl.h>
8 #include <errno.h>
9 #include <sys/param.h>
10 #include <sys/uio.h>
11
12 #include "riff.h"
13 #include "grab-ng.h"
14
15 #if 0 /* debugging */
16 # define LIMIT_OPENDML (1024*1024)
17 #else
18 # define LIMIT_OPENDML (2000*1024*1024)
19 #endif
20
21 /*
22 * M$ vidcap avi video+audio layout
23 *
24 * riff avi
25 * list hdrl header
26 * avih avi header
27 * list strl video stream header
28 * strh
29 * strf
30 * list strl audio stream header
31 * strh
32 * strf
33 * istf ??? software
34 * idit ??? timestamp
35 * yunk ??? 4k page pad
36 * list movi data
37 * 00db video data
38 * yunk ??? 4k page pad
39 * [ ... ]
40 * 01wb audio data
41 * [ ... ]
42 * idx1 video frame index
43 *
44 */
45
46 /* ----------------------------------------------------------------------- */
47
48 #define TRAP(txt) fprintf(stderr,"%s:%d:%s\n",__FILE__,__LINE__,txt);exit(1);
49
50 #define size_strl_vids (sizeof(struct RIFF_strh) + \
51 sizeof(struct RIFF_strf_vids) + \
52 4*5)
53 #define size_strl_auds (sizeof(struct RIFF_strh) + \
54 sizeof(struct RIFF_strf_auds) + \
55 4*5)
56
57 static const struct AVI_HDR avi_hdr = {
58 {'R','I','F','F'}, 0, {'A','V','I',' '},
59 {'L','I','S','T'}, 0, {'h','d','r','l'},
60 {'a','v','i','h'}, AVI_SWAP4(sizeof(struct RIFF_avih)), {}
61 };
62
63 static const struct AVIX_HDR avix_hdr = {
64 {'R','I','F','F'}, 0, {'A','V','I','X'},
65 {'L','I','S','T'}, 0, {'m','o','v','i'},
66 };
67
68 static const struct AVI_HDR_VIDEO avi_hdr_video = {
69 {'L','I','S','T'}, AVI_SWAP4(size_strl_vids), {'s','t','r','l'},
70 {'s','t','r','h'}, AVI_SWAP4(sizeof(struct RIFF_strh)), {{'v','i','d','s'}},
71 {'s','t','r','f'}, AVI_SWAP4(sizeof(struct RIFF_strf_vids)), {}
72 };
73
74 static const struct AVI_HDR_AUDIO avi_hdr_audio = {
75 {'L','I','S','T'}, AVI_SWAP4(size_strl_auds), {'s','t','r','l'},
76 {'s','t','r','h'}, AVI_SWAP4(sizeof(struct RIFF_strh)), {{'a','u','d','s'}},
77 {'s','t','r','f'}, AVI_SWAP4(sizeof(struct RIFF_strf_auds)), {}
78 };
79
80 static const struct AVI_HDR_ODML avi_hdr_odml = {
81 {'L','I','S','T'}, AVI_SWAP4(sizeof(uint32_t) + 4*3), {'o','d','m','l'},
82 {'d','m','l','h'}, AVI_SWAP4(sizeof(uint32_t)),
83 };
84
85 static const struct AVI_DATA avi_data = {
86 {'L','I','S','T'}, 0, {'m','o','v','i'},
87 };
88
89 static const struct CHUNK_HDR frame_hdr = {
90 {'','','d','b'}, 0
91 };
92 static const struct CHUNK_HDR sound_hdr = {
93 {'','1','w','b'}, 0
94 };
95 static const struct CHUNK_HDR idx_hdr = {
96 {'i','d','x','1'}, 0
97 };
98
99 /* ----------------------------------------------------------------------- */
100
101 struct avi_video_priv {
102 const char handler[4];
103 const char compress[4];
104 const int bytesperpixel;
105 };
106
107 struct avi_handle {
108 /* file name+handle */
109 char file[MAXPATHLEN];
110 int fd;
111 struct iovec *vec;
112
113 /* format */
114 struct ng_video_fmt video;
115 struct ng_audio_fmt audio;
116
117 /* headers */
118 struct AVI_HDR avi_hdr;
119 struct AVIX_HDR avix_hdr;
120 struct AVI_HDR_ODML avi_hdr_odml;
121 struct AVI_HDR_AUDIO avi_hdr_audio;
122 struct AVI_HDR_VIDEO avi_hdr_video;
123 struct AVI_DATA avi_data;
124 struct CHUNK_HDR frame_hdr;
125 struct CHUNK_HDR sound_hdr;
126 struct CHUNK_HDR idx_hdr;
127
128 /* statistics -- first chunk */
129 int frames;
130 off_t hdr_size;
131 off_t audio_size;
132 off_t data_size;
133
134 /* statistics -- current chunk */
135 int bigfile;
136 int framesx;
137 off_t avix_start;
138 off_t datax_size;
139
140 /* statistics -- total */
141 int frames_total;
142
143 /* frame index */
144 struct IDX_RECORD *idx_array;
145 int idx_index, idx_count;
146 off_t idx_offset;
147 off_t idx_size;
148 };
149
150 /* ----------------------------------------------------------------------- */
151 /* idx1 frame index */
152
153 static void
154 avi_addindex(struct avi_handle *h, char *fourcc,int flags,int chunksize)
155 {
156 if (h->idx_index == h->idx_count) {
157 h->idx_count += 256;
158 h->idx_array = realloc(h->idx_array,h->idx_count*sizeof(struct IDX_RECORD));
159 }
160 memcpy(h->idx_array[h->idx_index].id,fourcc,4);
161 h->idx_array[h->idx_index].flags=AVI_SWAP4(flags);
162 h->idx_array[h->idx_index].offset=AVI_SWAP4(h->idx_offset-h->hdr_size-8);
163 h->idx_array[h->idx_index].size=AVI_SWAP4(chunksize);
164 h->idx_index++;
165 h->idx_offset += chunksize + sizeof(struct CHUNK_HDR);
166 }
167
168 static void
169 avi_writeindex(struct avi_handle *h)
170 {
171 /* write frame index */
172 h->idx_hdr.size = AVI_SWAP4(h->idx_index * sizeof(struct IDX_RECORD));
173 write(h->fd,&h->idx_hdr,sizeof(struct CHUNK_HDR));
174 write(h->fd,h->idx_array,h->idx_index*sizeof(struct IDX_RECORD));
175 h->idx_size += h->idx_index * sizeof(struct IDX_RECORD)
176 + sizeof(struct CHUNK_HDR);
177
178 /* update header */
179 h->avi_hdr.avih.flags |= AVI_SWAP4(AVIF_HASINDEX);
180 }
181
182 static void
183 avi_bigfile(struct avi_handle *h, int last)
184 {
185 off_t avix_end;
186
187 if (h->bigfile) {
188 /* finish this chunk */
189 avix_end = lseek(h->fd,0,SEEK_CUR);
190 lseek(h->fd,h->avix_start,SEEK_SET);
191 h->avix_hdr.riff_size = h->datax_size + 4*4;
192 h->avix_hdr.data_size = h->datax_size + 4;
193 write(h->fd,&h->avix_hdr,sizeof(struct AVIX_HDR));
194 lseek(h->fd,avix_end,SEEK_SET);
195 h->avix_start = avix_end;
196 } else {
197 h->avix_start = lseek(h->fd,0,SEEK_CUR);
198 }
199 if (last)
200 return;
201 h->bigfile++;
202 h->framesx = 0;
203 h->datax_size = 0;
204 write(h->fd,&h->avix_hdr,sizeof(struct AVIX_HDR));
205 if (ng_debug)
206 fprintf(stderr,"avi bigfile #%d\n",h->bigfile);
207 }
208
209 /* ----------------------------------------------------------------------- */
210
211 static void
212 avi_write_header(struct avi_handle *h)
213 {
214 off_t curpos;
215
216 /* fill in some statistic values ... */
217 h->avi_hdr.riff_size = AVI_SWAP4(h->hdr_size+h->data_size+h->idx_size);
218 h->avi_hdr.hdrl_size = AVI_SWAP4(h->hdr_size - 4*5);
219 h->avi_hdr.avih.frames = AVI_SWAP4(h->frames);
220 if (h->video.fmtid != VIDEO_NONE)
221 h->avi_hdr_video.strh.length = AVI_SWAP4(h->frames);
222 if (h->audio.fmtid != AUDIO_NONE)
223 h->avi_hdr_audio.strh.length =
224 AVI_SWAP4(h->audio_size/h->avi_hdr_audio.strh.scale);
225 h->avi_data.data_size = AVI_SWAP4(h->data_size);
226
227 /* ... and write header again */
228 curpos = lseek(h->fd,0,SEEK_CUR);
229 lseek(h->fd,0,SEEK_SET);
230 write(h->fd,&h->avi_hdr,sizeof(struct AVI_HDR));
231 if (h->video.fmtid != VIDEO_NONE)
232 write(h->fd,&h->avi_hdr_video,sizeof(struct AVI_HDR_VIDEO));
233 if (h->audio.fmtid != AUDIO_NONE)
234 write(h->fd,&h->avi_hdr_audio,sizeof(struct AVI_HDR_AUDIO));
235 if (h->video.fmtid != VIDEO_NONE) {
236 h->avi_hdr_odml.total_frames = h->frames_total;
237 write(h->fd,&h->avi_hdr_odml,sizeof(struct AVI_HDR_ODML));
238 }
239 write(h->fd,&h->avi_data,sizeof(struct AVI_DATA));
240 lseek(h->fd,curpos,SEEK_SET);
241 }
242
243 static void*
244 avi_open(char *filename, char *dummy,
245 struct ng_video_fmt *video, const void *priv_video, int fps,
246 struct ng_audio_fmt *audio, const void *priv_audio)
247 {
248 const struct avi_video_priv *pvideo = priv_video;
249 struct avi_handle *h;
250 int i,frame_bytes,depth,streams,rate,us_frame;
251
252 if (NULL == (h = malloc(sizeof(*h))))
253 return NULL;
254 if (NULL == filename)
255 return NULL;
256
257 /* init */
258 memset(h,0,sizeof(*h));
259 h->video = *video;
260 h->audio = *audio;
261 h->avi_hdr = avi_hdr;
262 h->avix_hdr = avix_hdr;
263 h->avi_hdr_odml = avi_hdr_odml;
264 h->avi_hdr_video = avi_hdr_video;
265 h->avi_hdr_audio = avi_hdr_audio;
266 h->avi_data = avi_data;
267 h->frame_hdr = frame_hdr;
268 h->sound_hdr = sound_hdr;
269 h->idx_hdr = idx_hdr;
270 h->vec = malloc(sizeof(struct iovec) * video->height);
271
272 strcpy(h->file,filename);
273 if (-1 == (h->fd = open(h->file,O_CREAT | O_RDWR | O_TRUNC, 0666))) {
274 fprintf(stderr,"open %s: %s\n",h->file,strerror(errno));
275 free(h);
276 return NULL;
277 }
278
279 /* general */
280 streams = 0;
281 rate = 0;
282 if (h->video.fmtid != VIDEO_NONE) {
283 streams++;
284 rate += pvideo->bytesperpixel * fps / 1000;
285 h->avi_hdr.avih.width = AVI_SWAP4(h->video.width);
286 h->avi_hdr.avih.height = AVI_SWAP4(h->video.height);
287 }
288 if (h->audio.fmtid != AUDIO_NONE) {
289 streams++;
290 rate += ng_afmt_to_channels[h->audio.fmtid] *
291 ng_afmt_to_bits[h->audio.fmtid] *
292 h->audio.rate / 8;
293 }
294 us_frame = (long long)1000000000/fps;
295 h->avi_hdr.avih.us_frame = AVI_SWAP4(us_frame);
296 h->avi_hdr.avih.bps = AVI_SWAP4(rate);
297 h->avi_hdr.avih.streams = AVI_SWAP4(streams);
298 h->hdr_size += write(h->fd,&h->avi_hdr,sizeof(struct AVI_HDR));
299
300 /* video */
301 if (h->video.fmtid != VIDEO_NONE) {
302 for (i = 0; i < 4; i++) {
303 h->avi_hdr_video.strh.handler[i] = pvideo->handler[i];
304 h->avi_hdr_video.strf.compression[i] = pvideo->compress[i];
305 }
306 frame_bytes = pvideo->bytesperpixel * h->video.width * h->video.height;
307 depth = ng_vfmt_to_depth[h->video.fmtid];
308 h->frame_hdr.size = AVI_SWAP4(frame_bytes);
309 h->avi_hdr_video.strh.scale = AVI_SWAP4(us_frame);
310 h->avi_hdr_video.strh.rate = AVI_SWAP4(1000000);
311 h->avi_hdr_video.strf.size = AVI_SWAP4(sizeof(avi_hdr_video.strf));
312 h->avi_hdr_video.strf.width = AVI_SWAP4(h->video.width);
313 h->avi_hdr_video.strf.height = AVI_SWAP4(h->video.height);
314 h->avi_hdr_video.strf.planes = AVI_SWAP2(1);
315 h->avi_hdr_video.strf.bit_cnt = AVI_SWAP2(depth ? depth : 24);
316 h->avi_hdr_video.strf.image_size = AVI_SWAP4(frame_bytes);
317 h->hdr_size += write(h->fd,&h->avi_hdr_video,sizeof(struct AVI_HDR_VIDEO));
318 }
319
320 /* audio */
321 if (h->audio.fmtid != AUDIO_NONE) {
322 h->avi_hdr_audio.strh.scale =
323 AVI_SWAP4(ng_afmt_to_channels[h->audio.fmtid] *
324 ng_afmt_to_bits[h->audio.fmtid] / 8);
325 h->avi_hdr_audio.strh.rate =
326 AVI_SWAP4(ng_afmt_to_channels[h->audio.fmtid] *
327 ng_afmt_to_bits[h->audio.fmtid] *
328 h->audio.rate / 8);
329 h->avi_hdr_audio.strh.samplesize =
330 AVI_SWAP4(ng_afmt_to_channels[h->audio.fmtid] *
331 ng_afmt_to_bits[h->audio.fmtid] / 8);
332 h->avi_hdr_audio.strf.format =
333 AVI_SWAP2(WAVE_FORMAT_PCM);
334 h->avi_hdr_audio.strf.channels =
335 AVI_SWAP2(ng_afmt_to_channels[h->audio.fmtid]);
336 h->avi_hdr_audio.strf.rate =
337 AVI_SWAP4(h->audio.rate);
338 h->avi_hdr_audio.strf.av_bps =
339 AVI_SWAP4(ng_afmt_to_channels[h->audio.fmtid] *
340 ng_afmt_to_bits[h->audio.fmtid] *
341 h->audio.rate / 8);
342 h->avi_hdr_audio.strf.blockalign =
343 AVI_SWAP2(ng_afmt_to_channels[h->audio.fmtid] *
344 ng_afmt_to_bits[h->audio.fmtid] / 8);
345 h->avi_hdr_audio.strf.size =
346 AVI_SWAP2(ng_afmt_to_bits[h->audio.fmtid]);
347 h->hdr_size += write(h->fd,&h->avi_hdr_audio,
348 sizeof(struct AVI_HDR_AUDIO));
349 }
350 if (h->video.fmtid != VIDEO_NONE)
351 h->hdr_size += write(h->fd,&h->avi_hdr_odml,sizeof(struct AVI_HDR_ODML));
352
353 /* data */
354 if (-1 == write(h->fd,&h->avi_data,sizeof(struct AVI_DATA))) {
355 fprintf(stderr,"write %s: %s\n",h->file,strerror(errno));
356 free(h);
357 return NULL;
358 }
359 h->data_size = 4; /* list type */
360
361 h->idx_index = 0;
362 h->idx_offset = h->hdr_size + sizeof(struct AVI_DATA);
363
364 avi_write_header(h);
365 return h;
366 }
367
368 static int
369 avi_video(void *handle, struct ng_video_buf *buf)
370 {
371 struct avi_handle *h = handle;
372 struct iovec *line;
373 int y,bpl,size=0;
374
375 size = (buf->size + 3) & ~3;
376 h->frame_hdr.size = AVI_SWAP4(size);
377 if (-1 == write(h->fd,&h->frame_hdr,sizeof(struct CHUNK_HDR))) {
378 fprintf(stderr,"write %s: %s\n",h->file,strerror(errno));
379 return -1;
380 }
381 switch (h->video.fmtid) {
382 case VIDEO_RGB15_LE:
383 case VIDEO_BGR24:
384 bpl = h->video.width * ng_vfmt_to_depth[h->video.fmtid] / 8;
385 for (line = h->vec, y = h->video.height-1;
386 y >= 0; line++, y--) {
387 line->iov_base = ((unsigned char*)buf->data) + y * bpl;
388 line->iov_len = bpl;
389 }
390 if (-1 == writev(h->fd,h->vec,h->video.height)) {
391 fprintf(stderr,"writev %s: %s\n",h->file,strerror(errno));
392 return -1;
393 }
394 break;
395 case VIDEO_MJPEG:
396 case VIDEO_JPEG:
397 if (-1 == write(h->fd,buf->data,size)) {
398 fprintf(stderr,"write %s: %s\n",h->file,strerror(errno));
399 return -1;
400 }
401 break;
402 }
403 h->frames_total += 1;
404 if (!h->bigfile) {
405 avi_addindex(h,h->frame_hdr.id,0x12,size);
406 h->data_size += size + sizeof(struct CHUNK_HDR);
407 h->frames += 1;
408 } else {
409 h->datax_size += size + sizeof(struct CHUNK_HDR);
410 h->framesx += 1;
411 }
412 if ((h->bigfile ? h->datax_size : h->data_size) > LIMIT_OPENDML)
413 avi_bigfile(h,0);
414 return 0;
415 }
416
417 static int
418 avi_audio(void *handle, struct ng_audio_buf *buf)
419 {
420 struct avi_handle *h = handle;
421
422 h->sound_hdr.size = AVI_SWAP4(buf->size);
423 if (-1 == write(h->fd,&h->sound_hdr,sizeof(struct CHUNK_HDR))) {
424 fprintf(stderr,"write %s: %s\n",h->file,strerror(errno));
425 return -1;
426 }
427 if (-1 == write(h->fd,buf->data,buf->size)) {
428 fprintf(stderr,"write %s: %s\n",h->file,strerror(errno));
429 return -1;
430 }
431
432 if (!h->bigfile) {
433 avi_addindex(h,h->sound_hdr.id,0x0,buf->size);
434 h->data_size += buf->size + sizeof(struct CHUNK_HDR);
435 h->audio_size += buf->size;
436 } else {
437 h->datax_size += buf->size + sizeof(struct CHUNK_HDR);
438 }
439 return 0;
440 }
441
442 static int
443 avi_close(void *handle)
444 {
445 struct avi_handle *h = handle;
446
447 /* write frame index */
448 if (h->video.fmtid != VIDEO_NONE) {
449 if (!h->bigfile) {
450 avi_writeindex(h);
451 } else {
452 avi_bigfile(h,1);
453 h->idx_size = 0;
454 }
455 }
456
457 avi_write_header(h);
458 close(h->fd);
459 free(h->vec);
460 free(h);
461 return 0;
462 }
463
464 /* ----------------------------------------------------------------------- */
465 /* data structures describing our capabilities */
466
467 static struct avi_video_priv avi_rgb15 = {
468 bytesperpixel: 2,
469 };
470 static struct avi_video_priv avi_rgb24 = {
471 bytesperpixel: 3,
472 };
473 static struct avi_video_priv avi_mjpeg = {
474 handler: {'M','J','P','G'},
475 compress: {'M','J','P','G'},
476 bytesperpixel: 3,
477 };
478 static const struct ng_format_list avi_vformats[] = {
479 {
480 name: "rgb15",
481 ext: "avi",
482 fmtid: VIDEO_RGB15_LE,
483 priv: &avi_rgb15,
484 },{
485 name: "rgb24",
486 ext: "avi",
487 fmtid: VIDEO_BGR24,
488 priv: &avi_rgb24,
489 },{
490 name: "mjpeg",
491 ext: "avi",
492 fmtid: VIDEO_MJPEG,
493 priv: &avi_mjpeg,
494 },{
495 name: "jpeg",
496 ext: "avi",
497 fmtid: VIDEO_JPEG,
498 priv: &avi_mjpeg,
499 },{
500 /* EOF */
501 }
502 };
503
504 static const struct ng_format_list avi_aformats[] = {
505 {
506 name: "mono8",
507 ext: "avi",
508 fmtid: AUDIO_U8_MONO,
509 },{
510 name: "mono16",
511 ext: "avi",
512 fmtid: AUDIO_S16_LE_MONO,
513 },{
514 name: "stereo",
515 ext: "avi",
516 fmtid: AUDIO_S16_LE_STEREO,
517 },{
518 /* EOF */
519 }
520 };
521
522 struct ng_writer avi_writer = {
523 name: "avi",
524 desc: "Microsoft AVI (RIFF) format",
525 combined: 1,
526 video: avi_vformats,
527 audio: avi_aformats,
528 wr_open: avi_open,
529 wr_video: avi_video,
530 wr_audio: avi_audio,
531 wr_close: avi_close,
532 };
533
534 extern void ng_plugin_init(void);
535 void ng_plugin_init(void)
536 {
537 ng_writer_register(NG_PLUGIN_MAGIC,__FILE__,&avi_writer);
538 }
539
|
This page was automatically generated by the
LXR engine.
|