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

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