1 /*
2 * rawvideo.c
3 *
4 * Routines to read & write raw video files (w/ interleaved audio)
5 *
6 * (C) 1997 Randall Hopper
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
10 * met: 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer. 2.
12 * Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
20 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 */
29 /* ******************** Include Files ************** */
30
31 #include <stdio.h>
32 #include <assert.h>
33 #include <string.h>
34 #include <stdlib.h>
35 #include <fcntl.h>
36 #include <assert.h>
37 #include <sys/types.h>
38 #include <sys/uio.h>
39 #include <unistd.h>
40 #include <X11/Intrinsic.h>
41 #include "tvdefines.h"
42 #include "tvutil.h"
43 #include "rawvideo.h"
44
45 /* ******************** Local defines ************** */
46
47 struct TV_RAW_VIDEO_FILE {
48 TV_BOOL reading;
49 int num_files;
50 int fd [ TV_RAW_MAX_FILES ];
51 char fname[ TV_RAW_MAX_FILES ][ MAXPATHLEN ];
52 TV_INT32 next_file;
53 TV_RAW_HEADER hdr;
54 };
55
56 #define NEXT_RAW_FILE(fd,rf) ( fd = rf->fd[ rf->next_file++ ], \
57 rf->next_file %= rf->num_files )
58
59 /* ******************** Private variables ************** */
60 /* ******************** Forward declarations ************** */
61 /* ******************** Function Definitions ************** */
62 /* ******************** Function Definitions ************** */
63
64 /**@BEGINFUNC**************************************************************
65
66 Prototype : TV_UINT32 TVRAWVIDEOCalcImageSize(
67 TV_RAW_IMAGE *img )
68
69 Purpose : Calculate the size (in bytes) of a raw image to read/write
70 based on the header.
71
72 Programmer : 13-Jan-98 Randall Hopper
73
74 Parameters : img - I: an image with the header populated
75
76 Returns : size (in bytes) of the image
77
78 Globals : None.
79
80 **@ENDFUNC*****************************************************************/
81
TVRAWVIDEOCalcImageSize(TV_RAW_IMAGE * img)82 TV_UINT32 TVRAWVIDEOCalcImageSize(
83 TV_RAW_IMAGE *img )
84 {
85 TV_PIXEL_GEOM *pg = &img->pix_geom;
86 TV_UINT32 num_pix = img->geom.w * img->geom.h;
87
88 switch ( img->pix_geom.type ) {
89 case TV_PIXELTYPE_RGB :
90 return num_pix * img->pix_geom.Bpp;
91
92 case TV_PIXELTYPE_YUV :
93 assert(( pg->samp_size[0] == 8 ) &&
94 ( pg->samp_size[1] == 8 ) &&
95 ( pg->samp_size[2] == 8 ));
96
97 /* FIXME: What if X/Y res isn't a multiple of sampling rate? */
98 /*printf( "num_pix = %ld\n"
99 "pg->samp_int_h = %ld,%ld , %ld,%ld , %ld,%ld\n",
100 num_pix,
101 pg->samp_int_h[0], pg->samp_int_v[0],
102 pg->samp_int_h[1], pg->samp_int_v[1],
103 pg->samp_int_h[2], pg->samp_int_v[2] );*/
104
105 return num_pix / (pg->samp_int_h[0] * pg->samp_int_v[0]) +
106 num_pix / (pg->samp_int_h[1] * pg->samp_int_v[1]) +
107 num_pix / (pg->samp_int_h[2] * pg->samp_int_v[2]);
108
109 default :
110 fprintf( stderr, "TVRAWVIDEOCalcImageSize: Unsup pix type\n" );
111 exit(1);
112 }
113 }
114
115
116 /**@BEGINFUNC**************************************************************
117
118 Prototype : TV_BOOL TVRAWVIDEOOpen(
119 char *filenames[],
120 TV_INT32 num_files,
121 TV_BOOL read,
122 TV_RAW_VIDEO_FILE **rf )
123
124 Purpose : Opens a raw video data file/files for read or write.
125
126 Data read/written will be interleaved among the listed
127 files.
128
129 Programmer : 19-Jul-97 Randall Hopper
130
131 Parameters : filenames - I: list of filenames to read from / write to
132 read - I: T = read; F = write
133 num_files - I: length of filenames[]
134 (must be less than TV_RAW_MAX_FILES)
135 rf - O: file handle (ADT)
136
137 Returns : T = Success; F = Failure
138
139 Globals : None.
140
141 **@ENDFUNC*****************************************************************/
142
TVRAWVIDEOOpen(char * filenames[],TV_INT32 num_files,TV_BOOL read,TV_RAW_VIDEO_FILE ** rf)143 TV_BOOL TVRAWVIDEOOpen( char *filenames[],
144 TV_INT32 num_files,
145 TV_BOOL read,
146 TV_RAW_VIDEO_FILE **rf )
147 {
148 TV_INT32 i;
149 TV_RAW_VIDEO_FILE *raw_f;
150 TV_BOOL ok = TRUE;
151 int flags;
152 mode_t mode;
153
154 /* Allocate file handle */
155 if ( (*rf = malloc( sizeof(**rf) )) == NULL )
156 TVUTILOutOfMemory();
157
158 raw_f = *rf;
159 memset( raw_f, '\0', sizeof( *raw_f ) );
160
161 /* Open files */
162 if ( read )
163 flags = O_RDONLY , mode = 0;
164 else
165 flags = O_CREAT|O_WRONLY|O_TRUNC, mode = 0666;
166
167 for ( i = 0; i < num_files; i++ ) {
168 strncat( raw_f->fname[i], filenames[i], sizeof( raw_f->fname[i] )-1 );
169
170 if ( (raw_f->fd[i] = open( raw_f->fname[i], flags, mode )) < 0 ) {
171 fprintf( stderr, "Failed to open %s for %s.\n", raw_f->fname[i],
172 read ? "read" : "write" );
173 ok = FALSE;
174 break;
175 }
176 }
177
178 /* If we failed to open files, close the ones we opened */
179 if ( !ok ) {
180 for ( i--; i >= 0; i-- ) {
181 close( raw_f->fd[i] );
182 if ( !read )
183 unlink( raw_f->fname[i] );
184 }
185 free( raw_f );
186 *rf = NULL;
187 }
188
189 /* Everything's ok. Finish setting up the file handle */
190 else
191 raw_f->reading = read,
192 raw_f->num_files = num_files,
193 raw_f->next_file = 0;
194
195 return ok;
196 }
197
198
199 /**@BEGINFUNC**************************************************************
200
201 Prototype : TV_BOOL TVRAWVIDEOClose(
202 TV_RAW_VIDEO_FILE **rf )
203
204 Purpose : Closes an open raw video data file/files.
205
206 Programmer : 19-Jul-97 Randall Hopper
207
208 Parameters : rf - I/O: open file handle (ADT)
209
210 Returns : T = Success; F = Failure
211
212 Globals : None.
213
214 **@ENDFUNC*****************************************************************/
215
TVRAWVIDEOClose(TV_RAW_VIDEO_FILE ** rf)216 TV_BOOL TVRAWVIDEOClose( TV_RAW_VIDEO_FILE **rf )
217 {
218 TV_INT32 i;
219
220 if ( !rf || !*rf ) {
221 fprintf( stderr, "TVRAWVIDEOClose: Bad arg\n" );
222 exit(1);
223 }
224
225 for ( i = 0; i < (*rf)->num_files; i++ )
226 close( (*rf)->fd[i] );
227 free( *rf );
228 *rf = NULL;
229
230 return TRUE;
231 }
232
233
234 /**@BEGINFUNC**************************************************************
235
236 Prototype : TV_BOOL TVRAWVIDEOHeaderWrite(
237 TV_RAW_VIDEO_FILE *rf,
238 TV_RAW_IMAGE *img,
239 TV_RAW_SOUND *snd )
240
241 Purpose : Write a header describing the frame fmt in a raw video
242 file.
243
244 Use 4k block sizes for speed and so we can write to raw
245 devices.
246
247 Programmer : 19-Jul-97 Randall Hopper
248
249 Parameters : rf - I: raw data file handle
250 img - I: defines format of images we'll write
251 snd - I: defines format of audio we'll write with images
252
253 Returns : T = Success; F = Error
254
255 Globals : None.
256
257 **@ENDFUNC*****************************************************************/
258
TVRAWVIDEOHeaderWrite(TV_RAW_VIDEO_FILE * rf,TV_RAW_IMAGE * img,TV_RAW_SOUND * snd)259 TV_BOOL TVRAWVIDEOHeaderWrite( TV_RAW_VIDEO_FILE *rf,
260 TV_RAW_IMAGE *img,
261 TV_RAW_SOUND *snd )
262 {
263 char buf[ 4096 ];
264 int fd;
265
266 assert( (sizeof(*img) + sizeof(*snd)) <= 4096 );
267 assert( !rf->reading );
268
269 NEXT_RAW_FILE(fd, rf);
270
271 memcpy( &rf->hdr.img, img, sizeof( rf->hdr.img ) );
272 memcpy( &rf->hdr.snd, snd, sizeof( rf->hdr.snd ) );
273
274 memcpy( buf, img, sizeof(*img) );
275 memcpy( buf + sizeof(*img), snd, sizeof(*snd) );
276
277 if ( write( fd, buf, 4096 ) < 0 )
278 return FALSE;
279 return TRUE;
280 }
281
282
283 /**@BEGINFUNC**************************************************************
284
285 Prototype : TV_BOOL TVRAWVIDEOHeaderRead(
286 TV_RAW_VIDEO_FILE *rf,
287 TV_RAW_IMAGE *img,
288 TV_RAW_SOUND *snd,
289 TV_BOOL *eof )
290
291 Purpose : Read a header describing the frame fmt in a raw video
292 file.
293
294 Use 4k block sizes for speed and so we can write to raw
295 devices.
296
297 Programmer : 19-Jul-97 Randall Hopper
298
299 Parameters : rf - I: raw data file handle
300 img - O: defines format of images in raw file
301 snd - O: defines format of audio we'll read with images
302 eof - O: T = EOF & no header; F = got header
303
304 Returns : T = Success; F = Error
305
306 Globals : None.
307
308 **@ENDFUNC*****************************************************************/
309
TVRAWVIDEOHeaderRead(TV_RAW_VIDEO_FILE * rf,TV_RAW_IMAGE * img,TV_RAW_SOUND * snd,TV_BOOL * eof)310 TV_BOOL TVRAWVIDEOHeaderRead( TV_RAW_VIDEO_FILE *rf,
311 TV_RAW_IMAGE *img,
312 TV_RAW_SOUND *snd,
313 TV_BOOL *eof )
314 {
315 char buf[ 4096 ];
316 int siz;
317 int fd;
318
319 assert( sizeof(*img)+sizeof(*snd) <= 4096 );
320 assert( rf->reading );
321
322 NEXT_RAW_FILE(fd, rf);
323
324 memcpy( &rf->hdr.img, img, sizeof( rf->hdr.img ) );
325 memcpy( &rf->hdr.snd, snd, sizeof( rf->hdr.snd ) );
326
327 *eof = FALSE;
328
329 if ( (siz = read( fd, buf, 4096 )) < 0 )
330 return FALSE;
331 else if ( siz != 4096 ) {
332 *eof = TRUE;
333 return TRUE;
334 }
335
336 memcpy( img, buf, sizeof(*img) );
337 memcpy( snd, buf+sizeof(*img), sizeof(*snd) );
338 img->buf = NULL;
339 snd->buf = NULL;
340 snd->bytes = 0;
341
342 return TRUE;
343 }
344
345
346 /**@BEGINFUNC**************************************************************
347
348 Prototype : TV_BOOL TVRAWVIDEOImageWrite(
349 TV_RAW_VIDEO_FILE *rf,
350 TV_RAW_IMAGE_HEADER *head,
351 TV_RAW_IMAGE *img,
352 TV_RAW_SOUND *snd )
353
354 Purpose : Write the pixels for an image (and any interleaved audio
355 for the frame) to disk FAST.
356
357 Use 4k block sizes for speed and so we can write to raw
358 devices.
359
360 Programmer : 19-Jul-97 Randall Hopper
361
362 Parameters : rf - I: raw data file handle
363 head - I: the per-image header we'll write
364 img - I: the image data
365 snd - I: any associated sound data to write with the image
366
367 Returns : T = Success; F = Error
368
369 Globals : None.
370
371 **@ENDFUNC*****************************************************************/
372
TVRAWVIDEOImageWrite(TV_RAW_VIDEO_FILE * rf,TV_RAW_IMAGE_HEADER * head,TV_RAW_IMAGE * img,TV_RAW_SOUND * snd)373 TV_BOOL TVRAWVIDEOImageWrite( TV_RAW_VIDEO_FILE *rf,
374 TV_RAW_IMAGE_HEADER *head,
375 TV_RAW_IMAGE *img,
376 TV_RAW_SOUND *snd )
377 {
378 static char buf1[ 4096 ],
379 buf2[ 4096 ],
380 buf3[ 4096 ];
381 struct iovec iov[5];
382 int iov_len = 0;
383 TV_INT32 siz_img = TVRAWVIDEOCalcImageSize(img),
384 siz_snd = snd->bytes,
385 bufp = 0,
386 imgp = 0,
387 sndp = 0,
388 siz;
389 int fd;
390
391 assert( !rf->reading );
392
393 NEXT_RAW_FILE(fd, rf);
394
395 /* Fill in header */
396 head->image_bytes = siz_img;
397 head->sound_bytes = snd->bytes;
398
399 /* 1. Build first block */
400 memcpy( buf1, head, sizeof( *head ) );
401 bufp += sizeof( *head );
402
403 siz = MIN( 4096 - bufp, siz_img - imgp );
404 memcpy( &buf1[ bufp ], &img->buf[ imgp ], siz );
405 imgp += siz, bufp += siz;
406
407 if ( bufp < 4096 ) {
408 siz = MIN( 4096 - bufp, siz_snd - sndp );
409 memcpy( &buf1[ bufp ], &snd->buf[ sndp ], siz );
410 sndp += siz, bufp += siz;
411 }
412
413 iov[ iov_len ].iov_base = buf1;
414 iov[ iov_len++ ].iov_len = 4096;
415
416 if (( imgp == siz_img ) && ( sndp == siz_snd )) goto WRITEIT;
417
418 /* 2. Write as much of the image as we can in one continguous chunk */
419 siz = (siz_img - imgp) / 4096 * 4096;
420 if ( siz > 0 ) {
421 iov[ iov_len ].iov_base = &img->buf[ imgp ];
422 iov[ iov_len++ ].iov_len = siz;
423 imgp += siz;
424 }
425
426 if (( imgp == siz_img ) && ( sndp == siz_snd )) goto WRITEIT;
427
428 /* 3. Build block joining image and sound data */
429 bufp = 0;
430
431 siz = MIN( 4096 - bufp, siz_img - imgp );
432 memcpy( &buf2[ bufp ], &img->buf[ imgp ], siz );
433 imgp += siz, bufp += siz;
434
435 if ( bufp < 4096 ) {
436 siz = MIN( 4096 - bufp, siz_snd - sndp );
437 memcpy( &buf2[ bufp ], &snd->buf[ sndp ], siz );
438 sndp += siz, bufp += siz;
439 }
440
441 iov[ iov_len ].iov_base = buf2;
442 iov[ iov_len++ ].iov_len = 4096;
443
444 if (( imgp == siz_img ) && ( sndp == siz_snd )) goto WRITEIT;
445
446 /* 4. Write as much of the sound as we can in one continguous chunk */
447 siz = (siz_snd - sndp) / 4096 * 4096;
448 if ( siz > 0 ) {
449 iov[ iov_len ].iov_base = &snd->buf[ sndp ];
450 iov[ iov_len++ ].iov_len = siz;
451 sndp += siz;
452 }
453
454 if (( imgp == siz_img ) && ( sndp == siz_snd )) goto WRITEIT;
455
456 /* 5. And finally, build block containing the last of the sound data */
457 bufp = 0;
458
459 siz = siz_snd - sndp;
460 if (( siz <= 0 || siz > 4096 ))
461 return FALSE;
462
463 memcpy( &buf3[ bufp ], &snd->buf[ sndp ], siz );
464 sndp += siz, bufp += siz;
465
466 iov[ iov_len ].iov_base = buf3;
467 iov[ iov_len++ ].iov_len = 4096;
468
469 WRITEIT:
470 assert( imgp == siz_img && sndp == siz_snd );
471
472 if ( writev( fd, iov, iov_len ) < 0 )
473 return FALSE;
474 return TRUE;
475 }
476
477
478 /**@BEGINFUNC**************************************************************
479
480 Prototype : TV_BOOL TVRAWVIDEOImageRead(
481 TV_RAW_VIDEO_FILE *rf,
482 TV_RAW_IMAGE_HEADER *head,
483 TV_RAW_IMAGE *img,
484 TV_RAW_SOUND *snd,
485 TV_BOOL *eof )
486
487 Purpose : Read the pixels for an image (and any interleaved audio
488 for the frame) from disk FAST.
489
490 Use 4k block sizes for speed and so we can write to raw
491 devices.
492
493 NOTE: snd->buf must be freed by the client!
494
495 Programmer : 19-Jul-97 Randall Hopper
496
497 Parameters : rf - I: raw data file handle
498 head - O: the per-image header we read
499 img - O: the image data
500 snd - O: any associated sound data read with the image
501 (NOTE: snd->buf must be freed by the client)
502 eof - O: T = EOF & no image; F = got header
503
504 Returns : T = Success; F = Error
505
506 Globals : None.
507
508 **@ENDFUNC*****************************************************************/
509
TVRAWVIDEOImageRead(TV_RAW_VIDEO_FILE * rf,TV_RAW_IMAGE_HEADER * head,TV_RAW_IMAGE * img,TV_RAW_SOUND * snd,TV_BOOL * eof)510 TV_BOOL TVRAWVIDEOImageRead( TV_RAW_VIDEO_FILE *rf,
511 TV_RAW_IMAGE_HEADER *head,
512 TV_RAW_IMAGE *img,
513 TV_RAW_SOUND *snd,
514 TV_BOOL *eof )
515 {
516 static char buf[4096];
517 TV_INT32 siz_img = TVRAWVIDEOCalcImageSize(img),
518 siz_snd = snd->bytes,
519 bufp = 0,
520 imgp = 0,
521 sndp = 0,
522 siz;
523 int fd;
524
525 assert( rf->reading );
526
527 NEXT_RAW_FILE(fd, rf);
528
529 snd->bytes = 0;
530 snd->buf = NULL;
531
532 /* 1. Read the first block and grab the header */
533 if ( (siz = read( fd, buf, 4096 )) < 0 )
534 return FALSE;
535 else if ( siz == 0 ) {
536 *eof = TRUE;
537 return TRUE;
538 }
539 else if ( siz != 4096 )
540 return FALSE;
541
542 memcpy( head, &buf, sizeof( *head ) );
543 bufp = sizeof( *head );
544
545 if ( head->image_bytes != siz_img )
546 return FALSE;
547
548 /* Prepare sound buffer */
549 if (( head->sound_bytes > 0 ) &&
550 ( (snd->buf = malloc( head->sound_bytes )) == NULL ))
551 TVUTILOutOfMemory();
552 siz_snd = snd->bytes = head->sound_bytes;
553
554 /* Distribute rest of first chunk */
555 siz = MIN( 4096 - bufp, siz_img - imgp );
556 memcpy( &img->buf[ imgp ], &buf[ bufp ], siz );
557 imgp += siz, bufp += siz;
558
559 if ( bufp < 4096 ) {
560 siz = 4096 - bufp;
561 memcpy( &snd->buf[ sndp ], &buf[ bufp ], siz );
562 sndp += siz;
563 }
564
565 if (( imgp == siz_img ) && ( sndp == siz_snd )) goto RETURN;
566
567 /* 2. Read as much of the first image as we can in one chunk */
568 siz = (siz_img - imgp) / 4096 * 4096;
569 if ( siz > 0 ) {
570 if ( read( fd, &img->buf[ imgp ], siz ) != siz )
571 return FALSE;
572 imgp += siz;
573 }
574
575 if (( imgp == siz_img ) && ( sndp == siz_snd )) goto RETURN;
576
577 /* 3. Read block joining image and sound data */
578 if ( read( fd, buf, 4096 ) != 4096 )
579 return FALSE;
580 bufp = 0;
581
582 siz = MIN( 4096 - bufp, siz_img - imgp );
583 memcpy( &img->buf[ imgp ], &buf[ bufp ], siz );
584 imgp += siz, bufp += siz;
585
586 if ( bufp < 4096 ) {
587 siz = MIN( 4096 - bufp, siz_snd - sndp );
588 memcpy( &snd->buf[ sndp ], &buf[ bufp ], siz );
589 sndp += siz, bufp += siz;
590 }
591
592 if (( imgp == siz_img ) && ( sndp == siz_snd )) goto RETURN;
593
594 /* 4. Read as much of the sound as we can in one continguous chunk */
595 siz = (siz_snd - sndp) / 4096 * 4096;
596 if ( siz > 0 ) {
597 if ( read( fd, &snd->buf[ sndp ], siz ) != siz )
598 return FALSE;
599 sndp += siz;
600 }
601
602 if (( imgp == siz_img ) && ( sndp == siz_snd )) goto RETURN;
603
604 /* 5. And finally, read block containing the last of the sound data */
605 if ( read( fd, buf, 4096 ) != 4096 )
606 return FALSE;
607 bufp = 0;
608
609 siz = siz_snd - sndp;
610 if (( siz <= 0 || siz > 4096 ))
611 return FALSE;
612
613 memcpy( &snd->buf[ sndp ], &buf[ bufp ], siz );
614 sndp += siz, bufp += siz;
615
616 RETURN:
617 assert( imgp == siz_img && sndp == siz_snd );
618 return TRUE;
619 }
620
621