/*===========================================================================* * jpeg.c * * procedures to deal with JPEG files * * EXPORTED PROCEDURES: * JMovie2JPEG * ReadJPEG * *===========================================================================*/ /* COPYRIGHT INFORMATION IS AT THE END OF THIS FILE */ /*==============* * HEADER FILES * *==============*/ #define _XOPEN_SOURCE /* Make sure stdio.h contains fileno() */ #include #include "all.h" /* With the lossless jpeg patch applied to the Jpeg library (ftp://ftp.wizards.dupont.com/pub/ImageMagick/delegates/ljpeg-6b.tar.gz), the name of min_DCT_scaled_size changes to min_codec_data_unit, for some reason. With this macro, we change it back. */ #define min_codec_data_unit min_DCT_scaled_size #include #undef min_codec_data_unit #include "mtypes.h" #include "frames.h" #include "prototypes.h" #include "param.h" #include "readframe.h" #include "fsize.h" #include "rgbtoycc.h" #include "jpeg.h" /* make it happier.... */ #undef DCTSIZE2 /* jcopy_sample_rows() is an internal routine in the JPEG library, not meant for use by us. We should figure out what the supported interface for this is and use it. The following is copied out of jpegint.h, which is part of the JPEG library source code. */ extern void jcopy_sample_rows JPP((JSAMPARRAY input_array, int source_row, JSAMPARRAY output_array, int dest_row, int num_rows, JDIMENSION num_cols)); #define HEADER_SIZE 607 /*JFIF header size used on output images*/ /*=======================================================================* * * * JMovie2JPEG * * * * Splits up a Parallax J_Movie into a set of JFIF image files * * * * RETURNS: nothing * * * * SIDE EFFECTS: none * * * * Contributed By James Boucher(jboucher@flash.bu.edu) * * Boston University Multimedia Communications Lab * * This code was adapted from the Berkeley Playone.c and Brian Smith's * * JGetFrame code after being modified on 10-7-93 by Dinesh Venkatesh * * of BU. * * This code converts a version 2 Parallax J_Movie into a * * set of JFIF compatible JPEG images. It works for all image * * sizes and qualities. * ************************************************************************/ void JMovie2JPEG(infilename,obase,start,end) char *infilename; /* input filename string */ char *obase; /* output filename base string=>obase##.jpg */ int start; /* first frame to be extracted */ int end; /* last frame to be extracted */ { FILE *inFile; /* Jmovie file pointer */ FILE *outFile; /* JPEG file pointer for output file */ int fd, i; /* input file descriptor and a counting variable*/ char ofname[256]; /* output filename string */ int Temp = 0, temp = 0; /* dummy variables */ int image_offset = 0; /* counting variable */ /* J_Movie header infomation */ int ver_no; /* version number - expected to be 2 */ int fps; /* frame rate - frames per second */ int no_frames; /* total number of frames in jmovie */ int bandwidth; /* bandwidth required for normal playback*/ int qfactor; /* quality factor used to scale Q matrix */ int mapsize; /* number of color map entries - 2^24 */ int audio_tracks; /* number of audio tracks ==1 */ int audiosize; /*number of bytes in audio tracks */ int *inoffsets; /* input frame offsets from start of jmovie*/ int width; /* image width */ int height; /* image height */ int size; /* total image size in bytes */ char op_code; /* jmovie op_code */ char jpeg_size[4]; /* jpeg data size */ static char junk[1000]; /* data sink for audio data */ /* The next array represents the default JFIF header for quality = 100 and size = 320x240. The values are adjusted as the J_Movie header is read. The default size of this array is set large so as to make room for the appending of the jpeg bitstream. It can be made smaller if you have a better idea of its expected size*/ static unsigned char inbuffer[300000] = { 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0xF0, 0x01, 0x40, 0x03, 0x01, 0x21, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xFF, 0xDB, 0x00, 0x84, 0x00, 0x10, 0x0B, 0x0C, 0x0E, 0x0C, 0x0A, 0x10, 0x0E, 0x0D, 0x0E, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, 0x1A, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25, 0x1D, 0x28, 0x3A, 0x33, 0x3D, 0x3C, 0x39, 0x33, 0x38, 0x37, 0x40, 0x48, 0x5C, 0x4E, 0x40, 0x44, 0x57, 0x45, 0x37, 0x38, 0x50, 0x6D, 0x51, 0x57, 0x5F, 0x62, 0x67, 0x68, 0x67, 0x3E, 0x4D, 0x71, 0x79, 0x70, 0x64, 0x78, 0x5C, 0x65, 0x67, 0x63, 0x01, 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2F, 0x1A, 0x1A, 0x2F, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0xFF, 0xC4, 0x01, 0xA2, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3F, 0x00 }; if(start > end) { fprintf(stderr,"bad frame numbers\n"); exit(1); } /* open J_Movie */ inFile = fopen(infilename, "rb"); if (inFile == NULL) { perror (infilename); exit (1); } /* get file descriptor */ fd = fileno(inFile); /* The following lines parse the jpeg_movie header and recover the */ /* relavant information */ fseek (inFile, (8*sizeof(char)),0); if (fread (&ver_no,sizeof(int),1,inFile) != 1) { perror("Error in reading version"); exit(1); } if(ver_no != 2){ perror("Unrecognized version - Quantization tables may be wrong\n"); } if (fread (&(fps),sizeof(int),1,inFile) != 1) { perror("Error in reading fps"); exit(1); } if (fread (&(no_frames),sizeof(int),1,inFile) != 1) { perror("Error in reading no_frames"); exit(1); } inoffsets = (int *)malloc(no_frames*sizeof(int)); if (fread (&(width),sizeof(int),1,inFile) != 1) { perror("Error in reading width"); exit(1); } /* set image width in JFIF header */ inbuffer[27] = (char)(0xFF & (width >> 8)); inbuffer[28] = (char)(0xFF & width); if (fread (&(height),sizeof(int), 1,inFile) != 1) { perror("Error in reading height"); exit(1); } /* set image height in JFIF header */ inbuffer[25] = (char)(0xFF & (height >> 8)); inbuffer[26] = (char)(0xFF & height); if (fread (&(bandwidth),sizeof(int),1,inFile) != 1) { perror("Error in reading bandwidth"); exit(1); } if (fread (&(qfactor),sizeof(int),1,inFile) != 1) { perror("Error in reading qfactor"); exit(1); } /* The default quality factor = 100, therefore, if our quality factor does not equal 100 we must scale the quantization matrices in the JFIF header*/ /* Note values are clipped to a max of 255 */ if(qfactor != 100){ for(Temp=44;Temp<108;Temp++){ temp= (inbuffer[Temp]*qfactor)/100; inbuffer[Temp] = (char)((temp<255) ? temp : 255); } for(Temp=109;Temp<173;Temp++){ temp = (inbuffer[Temp]*qfactor)/100; inbuffer[Temp] = (char)((temp<255) ? temp : 255); } } if (fread (&(mapsize),sizeof(int),1,inFile) != 1) { perror("Error in reading mapsize"); exit(1); } if (fread (&(image_offset),sizeof(int),1,inFile) != 1) { perror("Error in reading image offset"); exit(1); } if (fread (&(audio_tracks),sizeof(int),1,inFile) != 1) { perror("Error in reading audio tracks"); exit(1); } fread(junk,sizeof(int),1,inFile); if (fread (&(audiosize),sizeof(int),1,inFile) != 1) { perror("Error in reading audiosize"); exit(1); } fseek (inFile,(image_offset),0); if(no_frames <= end) { end = no_frames - 1; } for(i=0;iid,(int)(cinfo.image_width),(int)(cinfo.image_height)); /* Allocate memory for the raw YCbCr data to occupy*/ Frame_AllocYCC(mf); /*allocate space for mpeg frame*/ /* copy pointers to array structure- this make the following code more compact */ orig[0] = mf->orig_y; orig[1] = mf->orig_cb; orig[2] = mf->orig_cr; /* Note that we can use the info obtained from jpeg_read_header. */ /* Start decompressor */ jpeg_start_decompress(&cinfo); /* JSAMPLEs per row in output buffer */ /* collect component subsample values*/ for(cp=0,compptr = cinfo.comp_info;cph_samp_factor; v_samp[cp] = compptr->v_samp_factor; } /* calculate max subsample values*/ temp_h = (h_samp[0]h_samp_factor)/ max_h_samp); nrows[cp] = (JDIMENSION)((buffer_height*compptr->v_samp_factor)/ max_v_samp); scanarray[cp] = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, ncols[cp], nrows[cp]); } /* while (scan lines remain to be read) */ /* jpeg_read_scanlines(...); */ /* Here we use the library's state variable cinfo.output_scanline as the * loop counter, so that we don't have to keep track ourselves. */ while (cinfo.output_scanline < cinfo.output_height) { #ifdef JPEG4 (void) jpeg_read_raw_scanlines(&cinfo, scanarray, buffer_height); #else (void) jpeg_read_raw_data(&cinfo, scanarray, buffer_height); #endif /* alter subsample ratio's if neccessary */ if((h_samp[0]==2)&&(h_samp[1]==1)&&(h_samp[2]==1)&& (v_samp[0]==2)&&(v_samp[1]==1)&&(v_samp[2]==1)){ /* we are 4:1:1 as expected by the encoder*/ }else if((h_samp[0]==2)&&(h_samp[1]==1)&&(h_samp[2]==1)&& (v_samp[0]==1)&&(v_samp[1]==1)&&(v_samp[2]==1)){ /* must subsample 2:1 vertically and adjust params*/ for(ci=1; ci<3; ci++){ for(cp=0; cp<(buffer_height/2);cp=cp+1){ for(cd=0;cd to make it work with * newer jpeg library. * * $Header: /u/smoot/md/mpeg_encode/RCS/jpeg.c,v 1.6 1995/06/08 20:36:00 smoot Exp $ * $Log: jpeg.c,v $ * Revision 1.6 1995/06/08 20:36:00 smoot * added "b"'s to fopen()s for MSDOS * * Revision 1.5 1995/02/02 21:24:02 eyhung * slight cleanup of unused variables * * Revision 1.4 1995/01/19 23:08:33 eyhung * Changed copyrights * * Revision 1.3 1995/01/19 22:58:34 smoot * fixes (I dont know what) * * Revision 1.2 1994/11/12 02:11:50 keving * nothing * * Revision 1.1 1994/03/15 00:27:11 keving * nothing * * Revision 1.2 1993/12/22 19:19:01 keving * nothing * * Revision 1.1 1993/07/22 22:23:43 keving * nothing * */