/* * rawvideo.c * * Routines to read & write raw video files (w/ interleaved audio) * * (C) 1997 Randall Hopper * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ /* ******************** Include Files ************** */ #include #include #include #include #include #include #include #include #include #include #include "tvdefines.h" #include "tvutil.h" #include "rawvideo.h" /* ******************** Local defines ************** */ struct TV_RAW_VIDEO_FILE { TV_BOOL reading; int num_files; int fd [ TV_RAW_MAX_FILES ]; char fname[ TV_RAW_MAX_FILES ][ MAXPATHLEN ]; TV_INT32 next_file; TV_RAW_HEADER hdr; }; #define NEXT_RAW_FILE(fd,rf) ( fd = rf->fd[ rf->next_file++ ], \ rf->next_file %= rf->num_files ) /* ******************** Private variables ************** */ /* ******************** Forward declarations ************** */ /* ******************** Function Definitions ************** */ /* ******************** Function Definitions ************** */ /**@BEGINFUNC************************************************************** Prototype : TV_UINT32 TVRAWVIDEOCalcImageSize( TV_RAW_IMAGE *img ) Purpose : Calculate the size (in bytes) of a raw image to read/write based on the header. Programmer : 13-Jan-98 Randall Hopper Parameters : img - I: an image with the header populated Returns : size (in bytes) of the image Globals : None. **@ENDFUNC*****************************************************************/ TV_UINT32 TVRAWVIDEOCalcImageSize( TV_RAW_IMAGE *img ) { TV_PIXEL_GEOM *pg = &img->pix_geom; TV_UINT32 num_pix = img->geom.w * img->geom.h; switch ( img->pix_geom.type ) { case TV_PIXELTYPE_RGB : return num_pix * img->pix_geom.Bpp; case TV_PIXELTYPE_YUV : assert(( pg->samp_size[0] == 8 ) && ( pg->samp_size[1] == 8 ) && ( pg->samp_size[2] == 8 )); /* FIXME: What if X/Y res isn't a multiple of sampling rate? */ /*printf( "num_pix = %ld\n" "pg->samp_int_h = %ld,%ld , %ld,%ld , %ld,%ld\n", num_pix, pg->samp_int_h[0], pg->samp_int_v[0], pg->samp_int_h[1], pg->samp_int_v[1], pg->samp_int_h[2], pg->samp_int_v[2] );*/ return num_pix / (pg->samp_int_h[0] * pg->samp_int_v[0]) + num_pix / (pg->samp_int_h[1] * pg->samp_int_v[1]) + num_pix / (pg->samp_int_h[2] * pg->samp_int_v[2]); default : fprintf( stderr, "TVRAWVIDEOCalcImageSize: Unsup pix type\n" ); exit(1); } } /**@BEGINFUNC************************************************************** Prototype : TV_BOOL TVRAWVIDEOOpen( char *filenames[], TV_INT32 num_files, TV_BOOL read, TV_RAW_VIDEO_FILE **rf ) Purpose : Opens a raw video data file/files for read or write. Data read/written will be interleaved among the listed files. Programmer : 19-Jul-97 Randall Hopper Parameters : filenames - I: list of filenames to read from / write to read - I: T = read; F = write num_files - I: length of filenames[] (must be less than TV_RAW_MAX_FILES) rf - O: file handle (ADT) Returns : T = Success; F = Failure Globals : None. **@ENDFUNC*****************************************************************/ TV_BOOL TVRAWVIDEOOpen( char *filenames[], TV_INT32 num_files, TV_BOOL read, TV_RAW_VIDEO_FILE **rf ) { TV_INT32 i; TV_RAW_VIDEO_FILE *raw_f; TV_BOOL ok = TRUE; int flags; mode_t mode; /* Allocate file handle */ if ( (*rf = malloc( sizeof(**rf) )) == NULL ) TVUTILOutOfMemory(); raw_f = *rf; memset( raw_f, '\0', sizeof( *raw_f ) ); /* Open files */ if ( read ) flags = O_RDONLY , mode = 0; else flags = O_CREAT|O_WRONLY|O_TRUNC, mode = 0666; for ( i = 0; i < num_files; i++ ) { strncat( raw_f->fname[i], filenames[i], sizeof( raw_f->fname[i] )-1 ); if ( (raw_f->fd[i] = open( raw_f->fname[i], flags, mode )) < 0 ) { fprintf( stderr, "Failed to open %s for %s.\n", raw_f->fname[i], read ? "read" : "write" ); ok = FALSE; break; } } /* If we failed to open files, close the ones we opened */ if ( !ok ) { for ( i--; i >= 0; i-- ) { close( raw_f->fd[i] ); if ( !read ) unlink( raw_f->fname[i] ); } free( raw_f ); *rf = NULL; } /* Everything's ok. Finish setting up the file handle */ else raw_f->reading = read, raw_f->num_files = num_files, raw_f->next_file = 0; return ok; } /**@BEGINFUNC************************************************************** Prototype : TV_BOOL TVRAWVIDEOClose( TV_RAW_VIDEO_FILE **rf ) Purpose : Closes an open raw video data file/files. Programmer : 19-Jul-97 Randall Hopper Parameters : rf - I/O: open file handle (ADT) Returns : T = Success; F = Failure Globals : None. **@ENDFUNC*****************************************************************/ TV_BOOL TVRAWVIDEOClose( TV_RAW_VIDEO_FILE **rf ) { TV_INT32 i; if ( !rf || !*rf ) { fprintf( stderr, "TVRAWVIDEOClose: Bad arg\n" ); exit(1); } for ( i = 0; i < (*rf)->num_files; i++ ) close( (*rf)->fd[i] ); free( *rf ); *rf = NULL; return TRUE; } /**@BEGINFUNC************************************************************** Prototype : TV_BOOL TVRAWVIDEOHeaderWrite( TV_RAW_VIDEO_FILE *rf, TV_RAW_IMAGE *img, TV_RAW_SOUND *snd ) Purpose : Write a header describing the frame fmt in a raw video file. Use 4k block sizes for speed and so we can write to raw devices. Programmer : 19-Jul-97 Randall Hopper Parameters : rf - I: raw data file handle img - I: defines format of images we'll write snd - I: defines format of audio we'll write with images Returns : T = Success; F = Error Globals : None. **@ENDFUNC*****************************************************************/ TV_BOOL TVRAWVIDEOHeaderWrite( TV_RAW_VIDEO_FILE *rf, TV_RAW_IMAGE *img, TV_RAW_SOUND *snd ) { char buf[ 4096 ]; int fd; assert( (sizeof(*img) + sizeof(*snd)) <= 4096 ); assert( !rf->reading ); NEXT_RAW_FILE(fd, rf); memcpy( &rf->hdr.img, img, sizeof( rf->hdr.img ) ); memcpy( &rf->hdr.snd, snd, sizeof( rf->hdr.snd ) ); memcpy( buf, img, sizeof(*img) ); memcpy( buf + sizeof(*img), snd, sizeof(*snd) ); if ( write( fd, buf, 4096 ) < 0 ) return FALSE; return TRUE; } /**@BEGINFUNC************************************************************** Prototype : TV_BOOL TVRAWVIDEOHeaderRead( TV_RAW_VIDEO_FILE *rf, TV_RAW_IMAGE *img, TV_RAW_SOUND *snd, TV_BOOL *eof ) Purpose : Read a header describing the frame fmt in a raw video file. Use 4k block sizes for speed and so we can write to raw devices. Programmer : 19-Jul-97 Randall Hopper Parameters : rf - I: raw data file handle img - O: defines format of images in raw file snd - O: defines format of audio we'll read with images eof - O: T = EOF & no header; F = got header Returns : T = Success; F = Error Globals : None. **@ENDFUNC*****************************************************************/ TV_BOOL TVRAWVIDEOHeaderRead( TV_RAW_VIDEO_FILE *rf, TV_RAW_IMAGE *img, TV_RAW_SOUND *snd, TV_BOOL *eof ) { char buf[ 4096 ]; int siz; int fd; assert( sizeof(*img)+sizeof(*snd) <= 4096 ); assert( rf->reading ); NEXT_RAW_FILE(fd, rf); memcpy( &rf->hdr.img, img, sizeof( rf->hdr.img ) ); memcpy( &rf->hdr.snd, snd, sizeof( rf->hdr.snd ) ); *eof = FALSE; if ( (siz = read( fd, buf, 4096 )) < 0 ) return FALSE; else if ( siz != 4096 ) { *eof = TRUE; return TRUE; } memcpy( img, buf, sizeof(*img) ); memcpy( snd, buf+sizeof(*img), sizeof(*snd) ); img->buf = NULL; snd->buf = NULL; snd->bytes = 0; return TRUE; } /**@BEGINFUNC************************************************************** Prototype : TV_BOOL TVRAWVIDEOImageWrite( TV_RAW_VIDEO_FILE *rf, TV_RAW_IMAGE_HEADER *head, TV_RAW_IMAGE *img, TV_RAW_SOUND *snd ) Purpose : Write the pixels for an image (and any interleaved audio for the frame) to disk FAST. Use 4k block sizes for speed and so we can write to raw devices. Programmer : 19-Jul-97 Randall Hopper Parameters : rf - I: raw data file handle head - I: the per-image header we'll write img - I: the image data snd - I: any associated sound data to write with the image Returns : T = Success; F = Error Globals : None. **@ENDFUNC*****************************************************************/ TV_BOOL TVRAWVIDEOImageWrite( TV_RAW_VIDEO_FILE *rf, TV_RAW_IMAGE_HEADER *head, TV_RAW_IMAGE *img, TV_RAW_SOUND *snd ) { static char buf1[ 4096 ], buf2[ 4096 ], buf3[ 4096 ]; struct iovec iov[5]; int iov_len = 0; TV_INT32 siz_img = TVRAWVIDEOCalcImageSize(img), siz_snd = snd->bytes, bufp = 0, imgp = 0, sndp = 0, siz; int fd; assert( !rf->reading ); NEXT_RAW_FILE(fd, rf); /* Fill in header */ head->image_bytes = siz_img; head->sound_bytes = snd->bytes; /* 1. Build first block */ memcpy( buf1, head, sizeof( *head ) ); bufp += sizeof( *head ); siz = MIN( 4096 - bufp, siz_img - imgp ); memcpy( &buf1[ bufp ], &img->buf[ imgp ], siz ); imgp += siz, bufp += siz; if ( bufp < 4096 ) { siz = MIN( 4096 - bufp, siz_snd - sndp ); memcpy( &buf1[ bufp ], &snd->buf[ sndp ], siz ); sndp += siz, bufp += siz; } iov[ iov_len ].iov_base = buf1; iov[ iov_len++ ].iov_len = 4096; if (( imgp == siz_img ) && ( sndp == siz_snd )) goto WRITEIT; /* 2. Write as much of the image as we can in one continguous chunk */ siz = (siz_img - imgp) / 4096 * 4096; if ( siz > 0 ) { iov[ iov_len ].iov_base = &img->buf[ imgp ]; iov[ iov_len++ ].iov_len = siz; imgp += siz; } if (( imgp == siz_img ) && ( sndp == siz_snd )) goto WRITEIT; /* 3. Build block joining image and sound data */ bufp = 0; siz = MIN( 4096 - bufp, siz_img - imgp ); memcpy( &buf2[ bufp ], &img->buf[ imgp ], siz ); imgp += siz, bufp += siz; if ( bufp < 4096 ) { siz = MIN( 4096 - bufp, siz_snd - sndp ); memcpy( &buf2[ bufp ], &snd->buf[ sndp ], siz ); sndp += siz, bufp += siz; } iov[ iov_len ].iov_base = buf2; iov[ iov_len++ ].iov_len = 4096; if (( imgp == siz_img ) && ( sndp == siz_snd )) goto WRITEIT; /* 4. Write as much of the sound as we can in one continguous chunk */ siz = (siz_snd - sndp) / 4096 * 4096; if ( siz > 0 ) { iov[ iov_len ].iov_base = &snd->buf[ sndp ]; iov[ iov_len++ ].iov_len = siz; sndp += siz; } if (( imgp == siz_img ) && ( sndp == siz_snd )) goto WRITEIT; /* 5. And finally, build block containing the last of the sound data */ bufp = 0; siz = siz_snd - sndp; if (( siz <= 0 || siz > 4096 )) return FALSE; memcpy( &buf3[ bufp ], &snd->buf[ sndp ], siz ); sndp += siz, bufp += siz; iov[ iov_len ].iov_base = buf3; iov[ iov_len++ ].iov_len = 4096; WRITEIT: assert( imgp == siz_img && sndp == siz_snd ); if ( writev( fd, iov, iov_len ) < 0 ) return FALSE; return TRUE; } /**@BEGINFUNC************************************************************** Prototype : TV_BOOL TVRAWVIDEOImageRead( TV_RAW_VIDEO_FILE *rf, TV_RAW_IMAGE_HEADER *head, TV_RAW_IMAGE *img, TV_RAW_SOUND *snd, TV_BOOL *eof ) Purpose : Read the pixels for an image (and any interleaved audio for the frame) from disk FAST. Use 4k block sizes for speed and so we can write to raw devices. NOTE: snd->buf must be freed by the client! Programmer : 19-Jul-97 Randall Hopper Parameters : rf - I: raw data file handle head - O: the per-image header we read img - O: the image data snd - O: any associated sound data read with the image (NOTE: snd->buf must be freed by the client) eof - O: T = EOF & no image; F = got header Returns : T = Success; F = Error Globals : None. **@ENDFUNC*****************************************************************/ TV_BOOL TVRAWVIDEOImageRead( TV_RAW_VIDEO_FILE *rf, TV_RAW_IMAGE_HEADER *head, TV_RAW_IMAGE *img, TV_RAW_SOUND *snd, TV_BOOL *eof ) { static char buf[4096]; TV_INT32 siz_img = TVRAWVIDEOCalcImageSize(img), siz_snd = snd->bytes, bufp = 0, imgp = 0, sndp = 0, siz; int fd; assert( rf->reading ); NEXT_RAW_FILE(fd, rf); snd->bytes = 0; snd->buf = NULL; /* 1. Read the first block and grab the header */ if ( (siz = read( fd, buf, 4096 )) < 0 ) return FALSE; else if ( siz == 0 ) { *eof = TRUE; return TRUE; } else if ( siz != 4096 ) return FALSE; memcpy( head, &buf, sizeof( *head ) ); bufp = sizeof( *head ); if ( head->image_bytes != siz_img ) return FALSE; /* Prepare sound buffer */ if (( head->sound_bytes > 0 ) && ( (snd->buf = malloc( head->sound_bytes )) == NULL )) TVUTILOutOfMemory(); siz_snd = snd->bytes = head->sound_bytes; /* Distribute rest of first chunk */ siz = MIN( 4096 - bufp, siz_img - imgp ); memcpy( &img->buf[ imgp ], &buf[ bufp ], siz ); imgp += siz, bufp += siz; if ( bufp < 4096 ) { siz = 4096 - bufp; memcpy( &snd->buf[ sndp ], &buf[ bufp ], siz ); sndp += siz; } if (( imgp == siz_img ) && ( sndp == siz_snd )) goto RETURN; /* 2. Read as much of the first image as we can in one chunk */ siz = (siz_img - imgp) / 4096 * 4096; if ( siz > 0 ) { if ( read( fd, &img->buf[ imgp ], siz ) != siz ) return FALSE; imgp += siz; } if (( imgp == siz_img ) && ( sndp == siz_snd )) goto RETURN; /* 3. Read block joining image and sound data */ if ( read( fd, buf, 4096 ) != 4096 ) return FALSE; bufp = 0; siz = MIN( 4096 - bufp, siz_img - imgp ); memcpy( &img->buf[ imgp ], &buf[ bufp ], siz ); imgp += siz, bufp += siz; if ( bufp < 4096 ) { siz = MIN( 4096 - bufp, siz_snd - sndp ); memcpy( &snd->buf[ sndp ], &buf[ bufp ], siz ); sndp += siz, bufp += siz; } if (( imgp == siz_img ) && ( sndp == siz_snd )) goto RETURN; /* 4. Read as much of the sound as we can in one continguous chunk */ siz = (siz_snd - sndp) / 4096 * 4096; if ( siz > 0 ) { if ( read( fd, &snd->buf[ sndp ], siz ) != siz ) return FALSE; sndp += siz; } if (( imgp == siz_img ) && ( sndp == siz_snd )) goto RETURN; /* 5. And finally, read block containing the last of the sound data */ if ( read( fd, buf, 4096 ) != 4096 ) return FALSE; bufp = 0; siz = siz_snd - sndp; if (( siz <= 0 || siz > 4096 )) return FALSE; memcpy( &snd->buf[ sndp ], &buf[ bufp ], siz ); sndp += siz, bufp += siz; RETURN: assert( imgp == siz_img && sndp == siz_snd ); return TRUE; }