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

Generated on Thu Aug 7 16:18:58 2008 for elphel by  doxygen 1.5.1