00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00048 #include <unistd.h>
00049 #include <stdio.h>
00050 #include <stdlib.h>
00051 #include <signal.h>
00052 #include <fcntl.h>
00053 #include <sys/uio.h>
00054 #include <errno.h>
00055 #include <sys/types.h>
00056 #include <sys/socket.h>
00057 #include <sys/stat.h>
00058
00059
00060 #include <time.h>
00061 #include <string.h>
00062
00063 #include <netinet/in.h>
00064 #include <sys/mman.h>
00065 #include <sys/ioctl.h>
00066
00067 #include <asm/elphel/c313a.h>
00068 #include <asm/elphel/ext353.h>
00069 #include <asm/byteorder.h>
00070
00071
00072 #include <ogg/ogg.h>
00073 #include "ogmstreams.h"
00074
00075 #include "camogm_mov.h"
00076 #include "camogm.h"
00077
00078 #define QUICKTIME_MIN_HEADER 0x300 // Quicktime header length (w/o index tables) enough to accomodate
00079
00080
00081
00082
00083
00085 const char hexStr[] = "0123456789abcdef";
00087 char qtSourceFileName_video[] = "/etc/qt_video";
00088 char qtSourceFileName_audio[] = "/etc/qt_audio";
00089
00090 char *qtSourceFileName = NULL;
00091 char comStr[1024];
00092 int width = 1280;
00093 int height = 1024;
00094 int nframes = 100;
00095 int sample_dur = 80;
00096 int samplesPerChunk = 10;
00097 int framesize = 80000;
00098 int timescale = 600;
00099 int * sizes;
00100 int iPos;
00101
00102 int ofd;
00103 int iFileLen;
00104 char * q_template = NULL;
00105 long headerSize = 0;
00106 const char *iFile = NULL;
00107
00108 unsigned long audio_rate = 0;
00109 unsigned short audio_channels = 0;
00110 int audio_timescale;
00111 long audio_duration;
00112
00113 int quicktime_template_parser (const char * i_iFile,
00114 int i_ofd,
00115 int i_width,
00116 int i_height,
00117 int i_nframes,
00118 int i_sample_dur,
00119 int i_samplesPerChunk,
00120 int i_framesize,
00121 int i_timescale,
00122 int * i_sizes,
00123 int data_start
00124 );
00125 void putBigEndian(unsigned long d, int l);
00126 int parse_special(void);
00127 int parse (int top);
00128
00131 int camogm_init_mov(void) {
00132 FILE* qt_header;
00133 int size;
00134 qtSourceFileName = qtSourceFileName_video;
00135 if(ctx_a.audio_present) {
00136 qtSourceFileName = qtSourceFileName_audio;
00137 }
00138 if((qt_header = fopen(qtSourceFileName, "r")) == NULL) {
00139 D0(fprintf (debug_file,"Error opening Quicktime header template %s for reading\n", qtSourceFileName));
00140 return -CAMOGM_FRAME_FILE_ERR;
00141 }
00142 fseek(qt_header, 0, SEEK_END);
00143 size = ftell(qt_header);
00144 if(!((q_template = malloc(size + 1)))) {
00145 D0(fprintf (debug_file,"Could not allocate %d bytes of memory for Quicktime header template\n", (size+1)));
00146 fclose(qt_header);
00147 return -CAMOGM_FRAME_MALLOC ;
00148 }
00149 fseek(qt_header, 0, SEEK_SET);
00150 if(fread(q_template, size, 1, qt_header) < 1) {
00151 D0(fprintf(debug_file, "Could not read %d bytes of Quicktime header template from %s\n", (size + 1), qtSourceFileName));
00152 free(q_template);
00153 q_template = NULL;
00154 fclose(qt_header);
00155 return -CAMOGM_FRAME_FILE_ERR;
00156 }
00157 q_template[size] = 0;
00158 return 0;
00159 }
00160
00161 void camogm_free_mov(void) {
00162 if(q_template) {
00163 free(q_template);
00164 q_template = NULL;
00165 }
00166 }
00167
00168 int camogm_start_mov(void) {
00169 state->frames_pos = 0;
00170
00171
00172 DA(fprintf(stderr, "max_frames == %d\n", state->max_frames););
00173 if(!((state->audio_samples_to_chunk = malloc(12 * 3))))
00174 return -CAMOGM_FRAME_MALLOC;
00175 state->audio_samples_to_chunk[0] = -1;
00176 state->audio_samples_to_chunk[1] = -1;
00177 state->audio_samples_to_chunk[2] = -1;
00178
00179 if(!((state->frames_len = (unsigned long *)malloc(4 * state->max_frames))))
00180 return -CAMOGM_FRAME_MALLOC;
00181 state->offset_current = 0;
00182
00183 sprintf(state->path, "%s%010ld_%06ld.mov", state->path_prefix, state->frame_params.timestamp_sec, state->frame_params.timestamp_usec);
00184 if(((state->ivf = open(state->path, O_RDWR | O_CREAT, 0777))) < 0){
00185 D0(fprintf(debug_file, "Error opening %s for writing, returned %d, errno=%d\n", state->path, state->ivf, errno));
00186 return -CAMOGM_FRAME_FILE_ERR;
00187 }
00188
00189
00190 int data_offset = QUICKTIME_MIN_HEADER + 16;
00191 data_offset += 4 * (state->max_frames);
00192 data_offset += 4 * (state->max_frames);
00193 if(ctx_a.audio_present) {
00194 data_offset += 4 * (state->max_frames);
00195 data_offset += 12 * 3;
00196 }
00197
00198
00199 state->frame_data_start = data_offset;
00200 lseek(state->ivf, state->frame_data_start, SEEK_SET);
00201 return 0;
00202 }
00203
00204 int camogm_frame_mov(void) {
00205 int i, j;
00206 ssize_t iovlen, l;
00207 struct iovec chunks_iovec[7];
00208 l = 0;
00209 for(i = 0; i < state->chunk_index - 1; i++) {
00210 chunks_iovec[i].iov_base = state->packetchunks[i + 1].chunk;
00211 chunks_iovec[i].iov_len = state->packetchunks[i + 1].bytes;
00212 l += chunks_iovec[i].iov_len;
00213 }
00214 iovlen = writev(state->ivf, chunks_iovec, state->chunk_index - 1);
00215 if(iovlen < l) {
00216 fprintf(stderr, "MOV: fail to save frame...\n");
00217 j = errno;
00218 D0(fprintf(debug_file, "writev error %d (returned %d, expected %d)\n", j, iovlen, l));
00219 close(state->ivf);
00220 state->ivf = -1;
00221 return -CAMOGM_FRAME_FILE_ERR;
00222 }
00223 state->frames_len[state->frames_pos] = l;
00224 state->frames_pos++;
00225
00226 state->offset_current += l;
00227
00228 if(state->frames_pos >= state->max_frames - 16)
00229 return -CAMOGM_FRAME_FILE_ERR;
00230
00231 return 0;
00232 }
00233
00234 int camogm_audio_mov(void *buf, int len, int slen) {
00235
00236 int i, j;
00237 ssize_t wrlen;
00238 wrlen = write(state->ivf, buf, len);
00239 if(wrlen < len) {
00240 j = errno;
00241 D0(fprintf(debug_file, "writev error %d (returned %d, expected %d)\n", j, wrlen, len));
00242 close(state->ivf);
00243 state->ivf = -1;
00244 return -CAMOGM_FRAME_FILE_ERR;
00245 }
00246 unsigned long k;
00247 k = len;
00248 k |= 0x80000000;
00249 state->frames_len[state->frames_pos] = k;
00250 state->frames_pos++;
00251
00252 if(state->audio_samples_to_chunk[0] == -1) {
00253 state->audio_samples_to_chunk[0] = slen;
00254 } else {
00255 state->audio_samples_to_chunk[1]= state->audio_samples_to_chunk[2];
00256 state->audio_samples_to_chunk[2]= slen;
00257 }
00258 state->offset_current += wrlen;
00259 state->audio_frameno++;
00260 state->audio_samples += slen;
00261
00262
00263 return 0;
00264 }
00265
00267 int camogm_end_mov(void){
00268 off_t l
00269
00270 ;
00271 timescale = 10000;
00272
00273 l = lseek(state->ivf, 0, SEEK_CUR) - (state->frame_data_start) + 8;
00274
00275
00276 lseek(state->ivf, 0, SEEK_SET);
00277 fprintf(stderr, "frameno == %d, frames_per_chunk == %d\n", state->frameno, state->frames_per_chunk);
00278 quicktime_template_parser(q_template,
00279 state->ivf,
00280 state->width,
00281 state->height,
00282 state->frameno,
00283 state->frame_period / (1000000 / timescale),
00284 state->frames_per_chunk,
00285 0,
00286 (int) ((float)timescale/(state->timescale)),
00287
00288 NULL,
00289 state->frame_data_start
00290 );
00291 close(state->ivf);
00292 state->ivf = -1;
00294 if(state->frames_len) {
00295 free(state->frames_len);
00296 state->frames_len = NULL;
00297 }
00298 if(state->audio_samples_to_chunk) {
00299 free(state->audio_samples_to_chunk);
00300 state->audio_samples_to_chunk = NULL;
00301 }
00302 return 0;
00303 }
00304
00305
00306
00307
00308
00309
00310
00311 void putBigEndian(unsigned long d, int l) {
00312 unsigned char od[4];
00313 od[3] = d;
00314 od[2] = d >> 8;
00315 od[1] = d >> 16;
00316 od[0] = d >> 24;
00317 if(l)
00318 write(ofd, &od[4 - l], l);
00319
00320 }
00321
00323 char * sfgets(char * str, int size, const char * stream, int * pos) {
00324 int l;
00325 const char *eol = strchr(&stream[*pos], '\n');
00326 if(!eol)
00327 eol = stream + (strlen(stream) - 1);
00328 l = (eol - stream) - (*pos);
00329
00330 if(l >= size)
00331 l = size - 1;
00332 memcpy(str, &stream[*pos], l);
00333 str[l] = '\0';
00334 *pos += l;
00335 return str;
00336 }
00337
00338 int parse_special(void) {
00339 time_t ltime;
00340 int n,j,l;
00341 unsigned long k;
00342 char str[256];
00343 char c;
00344 int i = 0;
00345 int gap;
00346
00347 while(((c = iFile[iPos++]) != ' ') && (c != '\t') && (c != '\n') && (c != '\r') && (c != '\0') && (i < 255) && (iPos < iFileLen))
00348 str[i++] = c;
00349 str[i] = 0;
00350
00351 D4(fprintf (debug_file, "parse_special, str=!%s\n",str));
00352
00353 if(strcmp(str, "mdata") == 0) { putBigEndian(headerSize, 4);return 0;}
00354 if(strcmp(str, "height") == 0) { putBigEndian(height,2);return 0;}
00355 if(strcmp(str, "width") == 0) { putBigEndian(width,2);return 0;}
00356 if(strcmp(str, "nframes") == 0) { putBigEndian(nframes,4);return 0;}
00357 if(strcmp(str, "timescale") == 0) { putBigEndian(timescale,4);return 0;}
00358 if(strcmp(str, "duration") == 0) { putBigEndian(nframes*sample_dur,4);return 0;}
00359
00360 if(strcmp(str, "audio_channels") == 0) { putBigEndian(audio_channels, 2); return 0;}
00361 if(strcmp(str, "audio_rate") == 0) { putBigEndian(audio_rate, 4); return 0;}
00362 if(strcmp(str, "audio_timescale") == 0) { putBigEndian(audio_timescale, 4); return 0;}
00363 if(strcmp(str, "audio_duration") == 0) { putBigEndian(audio_duration, 4); return 0;}
00364 if(strcmp(str, "audio_frames") == 0) { putBigEndian(state->audio_frameno, 4); return 0;}
00365 if(strcmp(str, "audio_samples") == 0) { putBigEndian(state->audio_samples, 4); return 0;}
00366 if(strcmp(str, "audio_bytes_per_frame") == 0) { putBigEndian(state->audio_channels * 2, 4); return 0;}
00367
00368 if(strcmp(str, "audio_stsz") == 0) {
00369 putBigEndian(state->audio_channels * 2, 4);
00370 putBigEndian(state->audio_samples, 4);
00371 return 0;
00372 }
00373 if(strcmp(str, "audio_stco") == 0) {
00374 long offset = 0;
00375 n = state->audio_frameno;
00376
00377 putBigEndian(n, 4);
00378 j = 0;
00379 for(i = 0; i < state->frames_pos; i++) {
00380 k = state->frames_len[i];
00381 if(k & 0x80000000) {
00382 l = offset;
00383 putBigEndian(headerSize + l, 4);
00384 j++;
00385 }
00386 offset += k & 0x7FFFFFFF;
00387 }
00388 if(j != n)
00389 fprintf(stderr, "MOV: wrong records for \"audio_stco\": have write: %d, need to write: %d\n", j, n);
00390 return 0;
00391 }
00392
00393 if(strcmp(str, "audio_stsc") == 0) {
00394 n = 0;
00395 for(i = 0; i < 3; i++) {
00396 if(state->audio_samples_to_chunk[i] != -1) {
00397 n++;
00398 }
00399 }
00400 putBigEndian(n, 4);
00401 putBigEndian(1, 4);
00402 putBigEndian(state->audio_samples_to_chunk[0], 4);
00403 putBigEndian(01, 4);
00404 n = 2;
00405 if(state->audio_samples_to_chunk[1] != -1) {
00406 putBigEndian(n, 4);
00407 putBigEndian(state->audio_samples_to_chunk[1], 4);
00408 putBigEndian(01, 4);
00409 n = state->audio_frameno;
00410 }
00411 if(state->audio_samples_to_chunk[2] != -1) {
00412 putBigEndian(n, 4);
00413 putBigEndian(state->audio_samples_to_chunk[2], 4);
00414 putBigEndian(01, 4);
00415 }
00416 return 0;
00417 }
00418 if(strcmp(str, "audio_samples_chunk") == 0) { putBigEndian(1, 4);return 0;}
00419
00420 if(strcmp(str, "frame_duration") == 0) { putBigEndian(sample_dur,4);return 0;}
00421 if(strcmp(str, "samples_chunk") == 0) { putBigEndian(samplesPerChunk, 4);return 0;}
00422
00423 if(strcmp(str, "sample_sizes") == 0) {
00424 j = 0;
00425 n = nframes;
00426 for(i = 0; i < state->frames_pos; i++) {
00427 k = state->frames_len[i];
00428 if((k & 0x80000000) == 0x0) {
00429 l = k;
00430 putBigEndian(l, 4);
00431 j++;
00432 }
00433 }
00434 if(j != n)
00435 fprintf(stderr, "MOV: wrong records for \"samples_sizes\": have write: %d, need to write: %d\n", j, n);
00436 return 0;
00437 }
00438 if(strcmp(str, "chunk_offsets") == 0) {
00439 long offset = 0;
00440 n = nframes;
00441 j = 0;
00442 putBigEndian(n, 4);
00443 for(i = 0; i < state->frames_pos; i++) {
00444 k = state->frames_len[i];
00445 if((k & 0x80000000) == 0x0) {
00446 l = offset;
00447 putBigEndian(headerSize + l, 4);
00448 j++;
00449 }
00450 offset += k & 0x7FFFFFFF;
00451 }
00452 if(j != n)
00453 fprintf(stderr, "MOV: wrong records for \"samples_sizes\": have write: %d, need to write: %d\n", j, n);
00454 return 0;
00455 }
00456
00457
00458 if(strcmp(str, "data_size") == 0) {
00459 gap = headerSize - lseek(ofd, 0, SEEK_CUR) - 8;
00460 if(gap > 0) {
00461 D4(fprintf (debug_file, "Inserting a skip tag to compensate for a gap (%d bytes) between the header and the frame data\n", gap));
00462 if(gap < 8) {
00463 D0(fprintf(debug_file, "not enough room to insret 'skip' tag - %d (need 8)\n", gap));
00464 return -1;
00465 }
00466 D4(fprintf(debug_file, "writing hex %x, %x bytes\n", gap, 4));
00467 putBigEndian(gap, 4);
00468 D4(fprintf(debug_file, "writing string <%s>\n","skip"));
00469 write(ofd, "skip", 4);
00470 lseek(ofd, gap - 8, SEEK_CUR);
00471 }
00472 if(sizes != NULL) {
00473 l = 0;
00474 for(i = 0; i < nframes; i++)
00475 l += sizes[i];
00476 D4(fprintf(debug_file, "writing hex %x, %x bytes\n", l, 4));
00477 putBigEndian(l, 4);
00478 } else
00479 putBigEndian(nframes * framesize, 4);
00480 return 0;
00481 }
00482 if(strcmp(str, "time") == 0) {
00483 time(<ime);
00484 ltime += 2082801600;
00485 putBigEndian(ltime, 4);
00486
00487 return 0;
00488 }
00489 return -1;
00490 }
00491
00492 int parse(int top) {
00493 long out_start, out_end;
00494 char c;
00495 unsigned long d, l;
00496 char *cp;
00497 D4(fprintf(debug_file, "parse(%x)\n", top));
00498 c = iFile[iPos++];
00499 D5(fprintf(debug_file, "%c", c));
00500 out_start = lseek(ofd, 0, SEEK_CUR);
00501
00502 if(!top)
00503 putBigEndian(0, 4);
00504 while((iPos < iFileLen ) && (c != '}')) {
00505 if((c != ' ') && (c != '\t') && (c != '\r') && (c != '\n')) {
00506 if(c == '!') {
00507 if(parse_special() < 0)
00508 return -1;
00509 }
00510
00511 else {
00512 if(c == '{') {
00513 if(parse(0) < 0)
00514 return -1;
00515
00516 } else {
00517 if(c == '#') {
00518 sfgets(comStr, sizeof(comStr), iFile, &iPos);
00519 } else {
00520 if(c == '\'') {
00521 sfgets(comStr, sizeof(comStr), iFile, &iPos);
00522 if((cp = strchr(comStr, 0x0a)) != NULL) cp[0] = 0;
00523 if((cp = strchr(comStr, 0x0d)) != NULL) cp[0] = 0;
00524 if((cp = strchr(comStr, '#')) != NULL) cp[0] = 0;
00525 cp = comStr + strlen(comStr) - 1;
00526 while((cp > comStr) && ((cp[0] == 0x20) || (cp[0] == 0x09)))
00527 cp--;
00528 cp[1] = 0;
00529 write(ofd, comStr, strlen(comStr));
00530 D4(fprintf(debug_file, "writing string <%s>\n", comStr));
00531 } else {
00532 if(strchr(hexStr, c)) {
00533 d = 0;
00534 l = 1;
00535 do {
00536 d = (d << 4) + (strchr(hexStr, c) - hexStr);
00537 l++;
00538 } while((iPos < iFileLen) && (l <= 8) && (strchr(hexStr, (c = iFile[iPos++]))));
00539 l = (l) >> 1;
00540 putBigEndian(d, l);
00541 D4(fprintf(debug_file, "writing hex %lx, %lx bytes\n", d, l));
00542 } else {
00543 if(c == '}') {
00544 break;
00545 } else {
00546 return -1;
00547 }
00548 }
00549 }
00550 }
00551 }
00552 }
00553 }
00554 c = iFile[iPos++];
00555 }
00556
00557 if(!top) {
00558 out_end = lseek(ofd, 0, SEEK_CUR);
00559 lseek(ofd, out_start, SEEK_SET);
00560 putBigEndian((out_end - out_start), 4);
00561 lseek(state->ivf, out_end, SEEK_SET);
00562 }
00563 return 0;
00564 }
00565
00566
00567 int quicktime_template_parser (const char * i_iFile,
00568 int i_ofd,
00569 int i_width,
00570 int i_height,
00571 int i_nframes,
00572 int i_sample_dur,
00573 int i_samplesPerChunk,
00574 int i_framesize,
00575 int i_timescale,
00576 int * i_sizes,
00577 int data_start
00578 ) {
00579 iFile= i_iFile;
00580 width= i_width;
00581 height= i_height;
00582 nframes= i_nframes;
00583 sample_dur= i_sample_dur;
00584 samplesPerChunk= i_samplesPerChunk;
00585 framesize= i_framesize;
00586 timescale= i_timescale;
00587 sizes= i_sizes;
00588 iPos=0;
00589 ofd= i_ofd;
00590 iFileLen= strlen(iFile);
00591 lseek(ofd, 0, SEEK_SET);
00592
00593 audio_timescale = state->audio_rate;
00594 audio_rate = audio_timescale;
00595 audio_rate <<= 16;
00596 audio_duration = state->audio_samples;
00597 audio_channels = state->audio_channels;
00598
00599 if(data_start)
00600 headerSize = data_start;
00601 D3(fprintf(debug_file, "PASS I\n"));
00602 while(iPos < iFileLen)
00603 parse(1);
00604 if(!headerSize)
00605 headerSize = lseek(ofd, 0, SEEK_CUR);
00606 iPos = 0;
00607 lseek(ofd, 0, SEEK_SET);
00608 D3(fprintf(debug_file, "PASS II\n"));
00609 while(iPos < iFileLen)
00610 parse(1);
00611 return 0;
00612 }