apps/camogm/camogm_mov.c

Go to the documentation of this file.
00001 /*!***************************************************************************
00002 *! FILE NAME  : camogm_mov.c
00003 *! DESCRIPTION: Provides writing to file compatible with Apple Quicktime(R) for camogm
00004 *!TODO: Nothing yet here, will be added ASAP
00005 *! Copyright (C) 2007 Elphel, Inc.
00006 *! -----------------------------------------------------------------------------**
00007 *!  This program is free software: you can redistribute it and/or modify
00008 *!  it under the terms of the GNU General Public License as published by
00009 *!  the Free Software Foundation, either version 3 of the License, or
00010 *!  (at your option) any later version.
00011 *!
00012 *!  This program is distributed in the hope that it will be useful,
00013 *!  but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 *!  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 *!  GNU General Public License for more details.
00016 *!
00017 *!  You should have received a copy of the GNU General Public License
00018 *!  along with this program.  If not, see <http://www.gnu.org/licenses/>.
00019 *! -----------------------------------------------------------------------------**
00020 *!
00021 *!  $Log: camogm_mov.c,v $
00022 *!  Revision 1.1.1.1  2008/11/27 20:04:01  elphel
00023 *!
00024 *!
00025 *!  Revision 1.4  2008/04/11 23:09:33  elphel
00026 *!  modified to handle kml generation
00027 *!
00028 *!  Revision 1.3  2007/11/19 17:00:20  elphel
00029 *!  removed wrong dependency
00030 *!
00031 *!  Revision 1.2  2007/11/19 03:23:21  elphel
00032 *!  7.1.5.5 Added support for *.mov files in camogm.
00033 *!
00034 *!  Revision 1.1  2007/11/16 08:49:57  elphel
00035 *!  Initial release of camogm - program to record video/image to the camera hard drive (or other storage)
00036 *!
00037 */
00039 #include <unistd.h>
00040 #include <stdio.h>
00041 #include <stdlib.h>
00042 #include <signal.h>
00043 #include <fcntl.h>
00044 #include <sys/uio.h>
00045 #include <errno.h>
00046 #include <sys/types.h>
00047 #include <sys/socket.h>
00048 #include <sys/stat.h>
00049 //#include <ctype.h>
00050 //#include <getopt.h>
00051 #include <time.h>
00052 #include <string.h>
00053 
00054 #include <netinet/in.h> /*little <-> big endian ?*/
00055 #include <sys/mman.h>           /* mmap */
00056 #include <sys/ioctl.h>
00057 
00058 #include <asm/elphel/c313a.h>
00059 #include <asm/elphel/ext353.h>
00060 #include <asm/byteorder.h>
00061 
00062 
00063 #include <ogg/ogg.h> // has to be before ogmstreams.h
00064 #include "ogmstreams.h" // move it to <>?
00065 
00066 #include "camogm_mov.h"
00067 #include "camogm.h"
00068 
00069 #define QUICKTIME_MIN_HEADER 0x300 // Quicktime header length (w/o index tables) enough to accomodate 
00070                                    // static data .
00071 
00072 
00074 const char hexStr[]="0123456789abcdef";
00075 const char qtSourceFileName[]= "/etc/qt_source";
00076 char comStr[1024];
00077 int     width=1280;
00078 int height=1024;
00079 int nframes=100;
00080 int sample_dur=80;
00081 int samplesPerChunk=10;
00082 int     framesize=80000;
00083 int     timescale=600;
00084 int * sizes;    // array of frame sizes
00085 int iPos; 
00086 //int oPos; //!position in the string "oFile"
00087 int ofd; // output file descriptor (file opened by the caller)
00088 int iFileLen;
00089 char * q_template=NULL;
00090 long headerSize=0;
00091 const char *iFile=NULL;
00092 
00093 
00094 int quicktime_template_parser (const char * i_iFile, 
00095            int i_ofd,   
00096            int i_width, // width in pixels
00097            int i_height,
00098            int i_nframes,
00099            int i_sample_dur,
00100            int i_samplesPerChunk,
00101            int i_framesize,
00102            int i_timescale,
00103            int * i_sizes,
00104            int   data_start // put zero if the header is written before data (no gap)
00105          );
00106 void putBigEndian(unsigned long d, int l);
00107 int parse_special(void);
00108 int parse (int top);
00111 int camogm_init_mov(void) {
00112   FILE* qt_header;
00113   int size;
00114   if ((qt_header= fopen (qtSourceFileName,"r"))==NULL) {
00115     D0(fprintf (debug_file,"Error opening Quicktime header template %s for reading\n", qtSourceFileName));
00116     return -CAMOGM_FRAME_FILE_ERR;
00117   }
00118   fseek(qt_header, 0, SEEK_END);
00119   size=ftell(qt_header);
00120   //malloc(4*state->max_frames);
00121   if (!((q_template=malloc(size+1)))) {
00122      D0(fprintf (debug_file,"Could not allocate %d bytes of memory for Quicktime header template\n", (size+1)));
00123      fclose(qt_header);
00124      return -CAMOGM_FRAME_MALLOC ;
00125   }
00126   fseek(qt_header, 0, SEEK_SET); //rewind
00127   if (fread (q_template,size,1,qt_header)<1) {
00128     D0(fprintf (debug_file,"Could not read %d bytes of Quicktime header template from %s\n", (size+1),qtSourceFileName));
00129     free(q_template);
00130     q_template=NULL;
00131     fclose(qt_header);
00132     return -CAMOGM_FRAME_FILE_ERR;
00133   }
00134   q_template[size]=0;
00135   return 0;
00136 }
00137 
00138 void camogm_free_mov(void) {
00139   if (q_template) {
00140     free (q_template);
00141     q_template=NULL;
00142   }
00143 }
00144 
00145 int camogm_start_mov(void) {
00146 
00148   if (!((state->frame_lengths=malloc(4*state->max_frames)))) return -CAMOGM_FRAME_MALLOC ;
00150    sprintf(state->path,"%s%010ld_%06ld.mov",state->path_prefix,state->frame_params.timestamp_sec,state->frame_params.timestamp_usec);
00151    if (((state->ivf=open(state->path,O_RDWR | O_CREAT, 0777)))<0){
00152      D0(fprintf (debug_file, "Error opening %s for writing, returned %d, errno=%d\n", state->path,state->ivf,errno));
00153      return -CAMOGM_FRAME_FILE_ERR;
00154    }
00157   state->frame_data_start=QUICKTIME_MIN_HEADER+16+ 4*(state->max_frames)+ ( 4*(state->max_frames))/(state->frames_per_chunk); // 8 bytes for "skip" tag
00158    lseek(state->ivf, state->frame_data_start, SEEK_SET);
00159   return 0;
00160 }
00161 
00162 int camogm_frame_mov(void){
00163    int i,j;
00164    ssize_t iovlen,l;
00165    struct iovec chunks_iovec[7];
00166    l=0;
00167    for (i=0; i< (state->chunk_index)-1; i++) {
00168       chunks_iovec[i].iov_base=state->packetchunks[i+1].chunk;
00169       chunks_iovec[i].iov_len= state->packetchunks[i+1].bytes;
00170       l+=chunks_iovec[i].iov_len;
00171    }
00172    iovlen=writev(state->ivf,chunks_iovec, (state->chunk_index)-1);
00173    if (iovlen < l) {
00174           j=errno;
00175           D0(fprintf(debug_file,"writev error %d (returned %d, expected %d)\n",j,iovlen,l));
00176           close (state->ivf);
00177           state->ivf=-1;
00178           return -CAMOGM_FRAME_FILE_ERR;
00179    }
00180    state->frame_lengths[state->frameno]=l;
00181    return 0;
00182 }
00184 int camogm_end_mov(void){
00185    off_t l/*,he;
00186    unsigned char mdat_tag[8];
00187    unsigned char skip_tag[]="\0\0\0\0skip"*/;
00188    timescale=10000; 
00189 
00190    l=lseek(state->ivf, 0, SEEK_CUR) - (state->frame_data_start)+8; 
00191 //   lseek(state->ivf, state->frame_data_start, SEEK_SET);
00192 // fill in the header in the beginning of the file
00193    lseek(state->ivf, 0, SEEK_SET);
00194    quicktime_template_parser (q_template, 
00195            state->ivf,   
00196            state->width, 
00197            state->height,
00198            state->frameno,
00199            state->frame_period/ (1000000/timescale),
00200            state->frames_per_chunk,
00201            0, 
00202            (int) ((float)timescale/(state->timescale)),
00203            state->frame_lengths, 
00204            state->frame_data_start
00205          );
00206 #if 0
00209    he=lseek(state->ivf, 0, SEEK_CUR);// just after the original header end
00210    l=state->frame_data_start-he; 
00211    D4(fprintf (debug_file,"Remaining gap between Quicktime header and the data is %d (it should be>=8) \n", (int) l));  
00212    lseek(state->ivf, he-8, SEEK_SET);
00213    read (state->ivf, mdat_tag,8);    
00214    lseek(state->ivf, state->frame_data_start-8, SEEK_SET);
00215    write(state->ivf, mdat_tag,8);
00216    skip_tag[0]=(l >> 24) & 0xff;
00217    skip_tag[1]=(l >> 16) & 0xff;
00218    skip_tag[2]=(l >>  8) & 0xff;
00219    skip_tag[3]=(l      ) & 0xff;
00220    lseek(state->ivf, he-8, SEEK_SET);
00221    write(state->ivf, skip_tag,8);
00222 #endif
00223    close (state->ivf);
00224    state->ivf=-1;
00226   if (state->frame_lengths) {
00227      free(state->frame_lengths);
00228      state->frame_lengths=NULL;
00229   }
00230   return 0;
00231 }
00232 
00233 /*
00234  starts with the input file pointer just after the opening "{",
00235  and output file - at the beginning of it's output
00236  on exit - input pointer - after closing "}", output after it's output
00237 
00238 */
00239 
00240 void putBigEndian(unsigned long d, int l) {
00241 unsigned char od[4];
00242    od[3]=d;
00243    od[2]=d >> 8;
00244    od[1]=d >> 16;
00245    od[0]=d >> 24;
00246    if (l) write (ofd, &od[4-l], l);
00247 //    oPos+=l;
00248 }
00250 char * sfgets(char * str, int size, const char * stream, int * pos) {
00251      int l;
00252      const char * eol=strchr(&stream[*pos],'\n');
00253      if (!eol) eol=stream+(strlen(stream)-1); 
00254      l=(eol-stream)-(*pos);
00255 //     if (l >= size) eol=stream+ (*pos+size-1);
00256      if (l >= size) l=size-1;
00257      memcpy(str, &stream[*pos],l);
00258      str[l]='\0';
00259      *pos+=l;
00260     return str;
00261 }
00262 
00263 int parse_special(void) {
00264         
00265    time_t ltime;
00266          int n,j,l;
00267         char str[256];
00268         char c;
00269         int i=0;
00270    int gap;
00271 //      while (((c=fgetc(infile))!=0x20) && (c!=0x09) && (c!=0x0a) && (c!=0x0d) && (c!=0x0) && (i<255) && ( feof(infile) == 0 )) str[i++]=c;
00272         while (((c=iFile[iPos++])!=0x20) && (c!=0x09) && (c!=0x0a) && (c!=0x0d) && (c!=0x0) && (i<255) && ( iPos<iFileLen )) str[i++]=c;
00273         str[i]=0;
00274 
00275   D4(fprintf (debug_file, "parse_special, str=!%s\n",str));
00276 
00277         if (strcmp(str,"mdata")==0)  {putBigEndian(headerSize, 4);return 0;} // will put zeroes on pass 1
00278         if (strcmp(str,"height")==0) {putBigEndian(height,2);return 0;}
00279         if (strcmp(str,"width")==0)  {putBigEndian(width,2);return 0;}
00280         if (strcmp(str,"width")==0)  {putBigEndian(width,2);return 0;}
00281         if (strcmp(str,"nframes")==0)  {putBigEndian(nframes,4);return 0;}
00282         if (strcmp(str,"timescale")==0)  {putBigEndian(timescale,4);return 0;}
00283         if (strcmp(str,"duration")==0)  {putBigEndian(nframes*sample_dur,4);return 0;}
00284         if (strcmp(str,"frame_duration")==0)  {putBigEndian(sample_dur,4);return 0;}
00285         if (strcmp(str,"samples_chunk")==0)  {putBigEndian(samplesPerChunk, 4);return 0;} // will put zeroes on pass 1
00286         if (strcmp(str,"sample_sizes")==0)  {
00287        if (sizes!=NULL) for (i=0;i<nframes;i++) putBigEndian(sizes[i],4);
00288        else             for (i=0;i<nframes;i++) putBigEndian(framesize,4);
00289            return 0;
00290         }
00291         if (strcmp(str,"chunk_offsets")==0)  {
00292        n= (nframes-1)/samplesPerChunk +1;
00293        putBigEndian(n,4);
00294        if (sizes!=NULL) {
00295          l=0;j=0;
00296          for (i=0; i<nframes;i++) {
00297            if (j==0) putBigEndian(headerSize+l,4);
00298            j++; if (j>=samplesPerChunk) j=0;
00299            l+=sizes[i];
00300          }
00301 
00302        } else for (i=0;i<n;i++) putBigEndian(headerSize+framesize*samplesPerChunk*i,4);
00303            return 0;
00304         }
00306         if (strcmp(str,"data_size")==0)  {
00307        gap=headerSize-lseek(ofd,0,SEEK_CUR)-8;
00308        if (gap>0) { 
00309   D4(fprintf (debug_file, "Inserting a skip tag to compensate for a gap (%d bytes) between the header and the frame data\n",gap));
00310           if (gap<8) {
00311   D0(fprintf (debug_file, "not enough room to insret 'skip' tag - %d (need 8)\n",gap));
00312              return -1; 
00313           }
00314   D4(fprintf (debug_file, "writing hex %x, %x bytes\n",gap,4));
00315           putBigEndian(gap, 4);
00316   D4(fprintf (debug_file, "writing string <%s>\n","skip"));
00317           write (ofd, "skip", 4);
00318           lseek(ofd,gap-8,SEEK_CUR); 
00319        }
00320        if (sizes!=NULL) {
00321          l=0;
00322          for (i=0; i<nframes;i++) l+=sizes[i];
00323   D4(fprintf (debug_file, "writing hex %x, %x bytes\n",l,4));
00324          putBigEndian(l,4);
00325        } else putBigEndian(nframes * framesize,4);
00326            return 0;
00327         }
00328         if (strcmp(str,"time")==0)  {
00329                 time (&ltime);
00330                 ltime+=2082801600;      // 1970->1904// 31,557,600 seconds/year
00331                 putBigEndian(ltime,4);return 0;
00332    }
00333         return -1;
00334 }
00335 
00336 
00337 
00338 int parse (int top) {   // if top - will not include length
00339         long out_start, out_end;
00340         char c;
00341         unsigned long d,l;
00342         char * cp;
00343   D4(fprintf (debug_file,"parse(%x)\n",top));
00344 //      c=fgetc(infile);
00345    c=iFile[iPos++];
00346   D5(fprintf (debug_file,"%c",c));
00347 // out_start=ftell (outfile);
00348 //   out_start=oPos;
00349 //   out_start=oPos=lseek(ofd,0,SEEK_CUR);
00350    out_start=lseek(ofd,0,SEEK_CUR);
00351 
00352         if (!top)  putBigEndian(0, 4);
00353 //   while (( feof(infile) == 0 ) && (c!='}')) {
00354    while (( iPos<iFileLen ) && (c!='}')) {
00355 // skip white spaces strchr
00356                 if ((c!=' ') && (c!=0x9) && (c!=0xa) && (c!=0xd)) {
00357                   if (c=='!') {
00358                      if (parse_special()<0) return -1;
00359                   }
00360 // children atoms
00361                   else if (c=='{') {
00362                     if (parse(0)<0) return -1;
00363 // skip comments
00364 //                } else if (c=='#')  fgets( comStr, sizeof(comStr), infile);
00365                   } else if (c=='#') sfgets( comStr, sizeof(comStr), iFile, &iPos);
00366                   else if (c=='\'') {
00367 //                      fgets ( comStr, sizeof(comStr), infile);
00368                    sfgets( comStr, sizeof(comStr), iFile, &iPos);
00369                         if ((cp=strchr(comStr,0x0a))!=NULL) cp[0]=0;
00370                         if ((cp=strchr(comStr,0x0d))!=NULL) cp[0]=0;
00371                         if ((cp=strchr(comStr,'#'))!=NULL) cp[0]=0;
00372                         cp=comStr+strlen(comStr)-1;
00373                         while ((cp>comStr) && ((cp[0]==0x20) || (cp[0]==0x09))) cp--;
00374                         cp[1]=0;
00375 //                      fwrite (comStr,1, strlen(comStr),outfile);
00376 //         memcpy(&oFile[oPos],comStr,strlen(comStr));
00377                         write (ofd, comStr, strlen(comStr));
00378   D4(fprintf (debug_file, "writing string <%s>\n",comStr));
00379                   } else if (strchr(hexStr,c)) {
00380                         d=0;
00381                         l=1;
00382                         do {
00383                                 d=(d<<4)+(strchr(hexStr,c)-hexStr);
00384                                 l++;
00385                         } while (( iPos<iFileLen ) && (l<=8) && (strchr(hexStr,(c=iFile[iPos++]))) );
00386                         l=(l)>>1;
00387                 putBigEndian(d, l);
00388 
00389   D4(fprintf (debug_file, "writing hex %lx, %lx bytes\n",d,l));
00390 
00391 
00392                   } else if ((c=='}')){
00393              break;
00394 
00395                   } else {
00396                     return -1;
00397                   }
00398 
00399         }
00400 //          c=fgetc(infile);
00401        c=iFile[iPos++];
00402 
00403         }       
00404 
00405         //      fread fseek ftell
00406         if (!top) {
00407 //  out_end=ftell (outfile);
00408 //     out_end=oPos;
00409      out_end=lseek(ofd,0,SEEK_CUR);
00410 //     fseek (outfile,out_start,SEEK_SET);
00411 //     oPos=out_start;
00412      lseek (ofd,out_start,SEEK_SET);
00413           putBigEndian((out_end-out_start), 4);
00414 //  fseek (outfile,out_end,SEEK_SET);
00415 //        oPos=out_end;
00416      lseek (state->ivf,out_end,SEEK_SET);
00417     }
00418         return 0;
00419 }
00420 
00421 
00422 int quicktime_template_parser (const char * i_iFile, 
00423            int i_ofd,   
00424            int i_width, // width in pixels
00425            int i_height,
00426            int i_nframes,
00427            int i_sample_dur,
00428            int i_samplesPerChunk,
00429            int i_framesize,
00430            int i_timescale,
00431            int * i_sizes,
00432            int   data_start // zero if dfata is not written yet (will be no gap)
00433          ) {
00434         iFile=           i_iFile;
00435         width=           i_width;
00436         height=          i_height;
00437         nframes=         i_nframes;
00438         sample_dur=      i_sample_dur;
00439         samplesPerChunk= i_samplesPerChunk;
00440         framesize=       i_framesize;
00441         timescale=       i_timescale;
00442         sizes=           i_sizes;
00443         iPos=0; 
00444         ofd=             i_ofd;
00445         iFileLen=        strlen(iFile);
00446         lseek (ofd,0,SEEK_SET);
00447         if (data_start) headerSize=data_start;
00448 //  int iFileLen=strlen(inFIle);
00449   D3(fprintf (debug_file, "PASS I\n"));
00450 
00451 //   while ( feof(infile) == 0 )   parse(1); // pass 1
00452    while ( iPos < iFileLen )   parse(1); // pass 1
00453 //   headerSize=ftell (outfile);
00454 //        fseek (outfile,0,SEEK_SET); // rewind for pass 2
00455 //        fseek (infile, 0,SEEK_SET); // 
00456 //  headerSize=oPos;
00457           if (!headerSize) headerSize=lseek(ofd,0,SEEK_CUR);
00458           iPos=0;
00459 //        oPos=0;
00460           lseek (ofd,0,SEEK_SET);
00461 
00462   D3(fprintf (debug_file, "PASS II\n"));
00463 //   while ( feof(infile) == 0 )   parse(1); // pass 2
00464    while ( iPos < iFileLen )   parse(1); // pass 2
00465 
00466 //fclose (infile);
00467 //fclose (outfile);
00468 //   oFile[oPos]='\0';
00469         return 0;
00470 }

Generated on Fri Nov 28 00:06:21 2008 for elphel by  doxygen 1.5.1