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