2 ** cfr2tc v1.4 - July 26, 2006
\r
4 ** This program takes an avi file with a video stream containing null
\r
5 ** frames and outputs a new avi file containing the same video stream
\r
6 ** but with all null frames removed and a v1 or v2 timecode file.
\r
8 ** Copyright (C) 2005-2006 Kevin Stone
\r
10 ** This program is free software; you can redistribute it and/or modify
\r
11 ** it under the terms of the GNU General Public License as published by
\r
12 ** the Free Software Foundation; either version 2 of the License, or
\r
13 ** (at your option) any later version.
\r
15 ** This program is distributed in the hope that it will be useful,
\r
16 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
17 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
18 ** GNU General Public License for more details.
\r
20 ** You should have received a copy of the GNU General Public License
\r
21 ** along with this program; if not, write to the Free Software
\r
22 ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
\r
25 #include <windows.h>
\r
30 #define VERSION "v1.4"
\r
32 typedef DWORD FOURCC;
\r
48 CHUNKHEADER::CHUNKHEADER() : ckID(0), ckSize(0), ckData(0), ckDataSize(0) {};
\r
49 CHUNKHEADER::~CHUNKHEADER() { if (ckData) free(ckData); }
\r
59 LISTHEADER::LISTHEADER() : lstID(0), lstSize(0), lstType(0), chkHd(0) {};
\r
60 LISTHEADER::~LISTHEADER() { if (chkHd) delete chkHd; }
\r
61 LISTHEADER& LISTHEADER::operator=(LISTHEADER& ob2)
\r
64 lstSize = ob2.lstSize;
\r
65 lstType = ob2.lstType;
\r
66 if (chkHd) delete chkHd;
\r
67 chkHd = new CHUNKHEADER();
\r
68 chkHd->ckID = ob2.chkHd->ckID;
\r
69 chkHd->ckSize = ob2.chkHd->ckSize;
\r
70 chkHd->ckDataSize = ob2.chkHd->ckDataSize;
\r
71 if (chkHd->ckData) free(chkHd->ckData);
\r
72 chkHd->ckData = malloc(chkHd->ckDataSize);
\r
73 memcpy(chkHd->ckData,ob2.chkHd->ckData,chkHd->ckDataSize);
\r
83 TBUF::TBUF() : data(0), size(0) {};
\r
84 TBUF::~TBUF() { if (data) free(data); }
\r
94 DWORD dwInitialFrames;
\r
99 DWORD dwSuggestedBufferSize;
\r
101 DWORD dwSampleSize;
\r
112 DWORD dwMicroSecPerFrame;
\r
113 DWORD dwMaxBytesPerSec;
\r
114 DWORD dwPaddingGranularity;
\r
116 DWORD dwTotalFrames;
\r
117 DWORD dwInitialFrames;
\r
119 DWORD dwSuggestedBufferSize;
\r
122 DWORD dwReserved[4];
\r
127 DWORD dwGrandFrames;
\r
128 DWORD dwFuture[61];
\r
143 DWORD MAKE_FOURCC(char* fourCC)
\r
145 return (fourCC[0]|(fourCC[1]<<8)|(fourCC[2]<<16)|(fourCC[3]<<24));
\r
148 bool checkRiffHeader(FILE *f, FILE *o, RIFFHEADER *rh, bool write)
\r
150 if (!fread(rh,1,12,f)) return false;
\r
151 if (rh->riff != MAKE_FOURCC("RIFF")) return false;
\r
152 if (rh->fileType != MAKE_FOURCC("AVI ")) return false;
\r
153 if (write) { wpos += 12; if (!fwrite(rh,1,12,o)) return false; }
\r
157 bool scanForList(FILE *f, FILE *o, LISTHEADER *lh, char *fourCC, char *fourCCStop,
\r
158 bool force, bool write, bool &stop)
\r
163 if (!fread(&lh->lstID,1,2,f)) return false;
\r
166 if (!fread(((unsigned char*)&lh->lstID)+2,1,2,f)) return false;
\r
167 if (lh->lstID == 0x5453494C) break;
\r
168 if (write) { wpos += 2; if (!fwrite(&lh->lstID,1,2,o)) return false; }
\r
169 lh->lstID = lh->lstID>>16;
\r
171 if (!fread(&lh->lstSize,4,1,f)) return false;
\r
172 if (!fread(&lh->lstType,4,1,f)) return false;
\r
173 if (lh->lstType != MAKE_FOURCC(fourCC))
\r
175 if (force) return false;
\r
176 if (lh->lstType == MAKE_FOURCC(fourCCStop))
\r
178 if (fseek(f,-12,SEEK_CUR)) return false;
\r
182 DWORD fsize = lh->lstSize-4-(lh->lstSize&1);
\r
185 if (tbuf.size <= fsize+12)
\r
187 if (tbuf.data) free(tbuf.data);
\r
188 tbuf.data = malloc(fsize+12);
\r
189 if (!tbuf.data) return false;
\r
190 tbuf.size = fsize+12;
\r
192 if (!fread(tbuf.data,fsize,1,f)) return false;
\r
193 if (!fwrite(&lh->lstID,4,1,o)) return false;
\r
194 if (!fwrite(&lh->lstSize,4,1,o)) return false;
\r
195 if (!fwrite(&lh->lstType,4,1,o)) return false;
\r
196 if (!fwrite(tbuf.data,fsize,1,o)) return false;
\r
201 if (fseek(f,fsize,SEEK_CUR))
\r
206 if (fseek(f,-12,SEEK_CUR)) return false;
\r
210 bool readLH(FILE *f, LISTHEADER *lh)
\r
212 if (!fread(&lh->lstID,4,1,f)) return false;
\r
213 if (!fread(&lh->lstSize,4,1,f)) return false;
\r
214 if (!fread(&lh->lstType,4,1,f)) return false;
\r
218 bool writeLH(FILE *o, LISTHEADER *lh)
\r
220 if (!fwrite(&lh->lstID,4,1,o)) return false;
\r
221 if (!fwrite(&lh->lstSize,4,1,o)) return false;
\r
222 if (!fwrite(&lh->lstType,4,1,o)) return false;
\r
227 bool readLHChunk(FILE *f, LISTHEADER *lh, char *fourCC, DWORD size)
\r
229 if (lh->chkHd) delete lh->chkHd;
\r
230 lh->chkHd = new CHUNKHEADER();
\r
231 if (!lh->chkHd) return false;
\r
232 if (!fread(&lh->chkHd->ckID,4,1,f)) return false;
\r
233 if (lh->chkHd->ckID != MAKE_FOURCC(fourCC)) return false;
\r
234 if (!fread(&lh->chkHd->ckSize,4,1,f)) return false;
\r
235 if (lh->chkHd->ckSize != size) return false;
\r
236 if (lh->chkHd->ckData) free(lh->chkHd->ckData);
\r
237 lh->chkHd->ckDataSize = lh->chkHd->ckSize+(lh->chkHd->ckSize&1);
\r
238 lh->chkHd->ckData = malloc(lh->chkHd->ckDataSize);
\r
239 if (!lh->chkHd->ckData) return false;
\r
240 if (!fread(lh->chkHd->ckData,lh->chkHd->ckSize,1,f)) return false;
\r
244 bool writeLHChunk(FILE *o, LISTHEADER *lh)
\r
246 if (!fwrite(&lh->chkHd->ckID,4,1,o)) return false;
\r
247 if (!fwrite(&lh->chkHd->ckSize,4,1,o)) return false;
\r
248 if (!fwrite(lh->chkHd->ckData,lh->chkHd->ckDataSize,1,o)) return false;
\r
249 wpos += 8+lh->chkHd->ckDataSize;
\r
253 bool scanForChunk(FILE *f, FILE *o, CHUNKHEADER *ch, char *fourCC, bool force, bool write)
\r
256 if (!fread(&ch->ckID,4,1,f)) return false;
\r
257 if (!fread(&ch->ckSize,4,1,f)) return false;
\r
258 if (ch->ckID == MAKE_FOURCC(fourCC) ||
\r
259 ((MAKE_FOURCC(fourCC)&0xFFFF0000) == 0x63640000 && (ch->ckID&0xFFFF0000) == 0x62640000) ||
\r
260 ((MAKE_FOURCC(fourCC)&0xFFFF0000) == 0x62640000 && (ch->ckID&0xFFFF0000) == 0x63640000))
\r
262 if (fseek(f, -8, SEEK_CUR)) return false;
\r
267 if (force) return false;
\r
268 DWORD pad_size = ch->ckSize+(ch->ckSize&1);
\r
271 if (tbuf.size <= pad_size+12)
\r
273 if (tbuf.data) free(tbuf.data);
\r
274 tbuf.data = malloc(pad_size+12);
\r
275 if (!tbuf.data) return false;
\r
276 tbuf.size = pad_size+12;
\r
278 if (!fread(tbuf.data,pad_size,1,f)) return false;
\r
279 if (!fwrite(&ch->ckID,4,1,o)) return false;
\r
280 if (!fwrite(&ch->ckSize,4,1,o)) return false;
\r
281 if (!fwrite(tbuf.data,pad_size,1,o)) return false;
\r
282 wpos += 8+pad_size;
\r
286 if (fseek(f,pad_size,SEEK_CUR))
\r
294 bool readChunk(FILE *f, CHUNKHEADER *ch)
\r
296 if (!fread(&ch->ckID,4,1,f)) return false;
\r
297 if (!fread(&ch->ckSize,4,1,f)) return false;
\r
298 DWORD pad_size = ch->ckSize+(ch->ckSize&1);
\r
299 if (ch->ckDataSize <= pad_size+12)
\r
301 if (ch->ckData) free(ch->ckData);
\r
302 ch->ckData = malloc(pad_size+12);
\r
303 if (!ch->ckData) return false;
\r
304 ch->ckDataSize = pad_size+12;
\r
306 if (!fread(ch->ckData,pad_size,1,f) && pad_size != 0) return false;
\r
310 bool readChunkHD(FILE *f, CHUNKHEADER *ch, bool seek)
\r
312 if (!fread(&ch->ckID,4,1,f)) return false;
\r
313 if (!fread(&ch->ckSize,4,1,f)) return false;
\r
314 if (seek) { if (fseek(f,ch->ckSize+(ch->ckSize&1),SEEK_CUR)) return false; }
\r
318 bool writeChunk(FILE *o, CHUNKHEADER *ch)
\r
320 if (!fwrite(&ch->ckID,4,1,o)) return false;
\r
321 if (!fwrite(&ch->ckSize,4,1,o)) return false;
\r
322 DWORD pad_size = ch->ckSize+(ch->ckSize&1);
\r
323 if (!fwrite(ch->ckData,pad_size,1,o)) return false;
\r
324 wpos += 8+pad_size;
\r
328 bool writeChunkHD(FILE *o, CHUNKHEADER *ch)
\r
330 if (!fwrite(&ch->ckID,4,1,o)) return false;
\r
331 if (!fwrite(&ch->ckSize,4,1,o)) return false;
\r
336 bool readIndexChunk(FILE *f, AVIOLDINDEX *index, int size)
\r
338 if (!fread(index,size,1,f)) return false;
\r
342 bool writeIndexChunk(FILE *o, AVIOLDINDEX *index, int size)
\r
344 if (!fwrite(index,size,1,o)) return false;
\r
349 void closeFiles(FILE *f, FILE *o, FILE *tcf, DWORD *repCount, DWORD *offsets,
\r
350 bool ul, char *oname, char *tcfname, char *fbuf, char *obuf)
\r
356 if (ul && oname) unlink(oname);
\r
361 if (ul && tcfname) unlink(tcfname);
\r
363 if (repCount) free(repCount);
\r
364 if (offsets) free(offsets);
\r
365 if (fbuf) _aligned_free(fbuf);
\r
366 if (obuf) _aligned_free(obuf);
\r
369 bool getInfo(FILE *f, DWORD &frameCount, DWORD &newFrameCount, double &fps, DWORD *&repCount,
\r
370 DWORD *&offsets, DWORD &nmovi_size, DWORD &nhdrl_size)
\r
374 /* Check for valid RIFF header. */
\r
376 if (!checkRiffHeader(f, NULL, &rh, false))
\r
378 printf("Invalid RIFF header (not an avi file).\n");
\r
382 /* Scan until first "LIST" fourcc. The first one should be "hdrl" type. */
\r
384 if (!scanForList(f, NULL, &hdrl, "hdrl", "FFFF", true, false, stop))
\r
386 printf("First LIST is invalid.\n");
\r
389 if (!readLH(f, &hdrl)) return false;
\r
390 nhdrl_size = hdrl.lstSize;
\r
392 /* Next should be the "avih" chunk with the main AVI Header. */
\r
393 if (!readLHChunk(f, &hdrl, "avih", sizeof(AVIMAINHEADER)))
\r
395 printf("Error reading avih CHUNK.\n");
\r
398 AVIMAINHEADER* am = (AVIMAINHEADER*)hdrl.chkHd->ckData;
\r
399 frameCount = am->dwTotalFrames;
\r
400 DWORD streamCount = am->dwStreams;
\r
402 /* Make sure that the file has an "idx1" index. */
\r
403 if (!(am->dwFlags&0x00000010))
\r
405 printf("File has no \"idx1\" index (dwFlags).\n");
\r
409 /* Make sure that AVIF_MUSTUSEINDEX is not set. */
\r
410 if (am->dwFlags&0x00000020)
\r
412 printf("Cannot use a file with AVIF_MUSTUSEINDEX flag set.\n");
\r
416 /* Next should be an "strl" list for each stream. */
\r
418 int count = 0, vstream = -20;
\r
419 for (DWORD j=0; j<streamCount; ++j)
\r
422 if (!scanForList(f, NULL, &tstrl, "strl", "FFFF", true, false, stop))
\r
424 printf("strl LIST is invalid or was not found.\n");
\r
427 if (!readLH(f, &tstrl)) return false;
\r
428 /* Next should be the "strh" chunk which is an AVISTREAMHEADER structure. */
\r
429 if (!readLHChunk(f, &tstrl, "strh", sizeof(AVISTREAMHEADER)))
\r
431 printf("Error reading strh CHUNK.\n");
\r
434 /* See if it is a video stream. */
\r
435 if (((AVISTREAMHEADER*)tstrl.chkHd->ckData)->fccType == MAKE_FOURCC("vids"))
\r
441 printf("More than 1 video stream present.\n");
\r
446 else nhdrl_size -= 8+tstrl.lstSize;
\r
447 if (fseek(f,tstrl.lstSize-12-sizeof(AVISTREAMHEADER),SEEK_CUR))
\r
452 printf("No video stream present.\n");
\r
455 AVISTREAMHEADER* ash = (AVISTREAMHEADER*)strl.chkHd->ckData;
\r
456 fps = double(ash->dwRate)/double(ash->dwScale);
\r
458 /* Make sure that AVISF_VIDEO_PALCHANGES is not set. */
\r
459 if (ash->dwFlags&0x00010000)
\r
461 printf("Cannot use a stream with AVISF_VIDEO_PALCHANGES flag set.\n");
\r
465 /* Next grab the AVI2 "odml" LIST. */
\r
467 if (!scanForList(f, NULL, &odml, "odml", "movi", false, false, stop))
\r
469 if (stop) { goto noodml; }
\r
470 printf("odml LIST was invalid or was not found.\n");
\r
473 if (!readLH(f, &odml)) return false;
\r
475 /* Now should be the "dmlh" CHUNK. */
\r
476 if (!readLHChunk(f, &odml, "dmlh", sizeof(AVIEXTHEADER)))
\r
478 printf("Error reading dmlh CHUNK.\n");
\r
482 /* Make sure frame counts match. */
\r
483 if (frameCount != ((AVIEXTHEADER*)odml.chkHd->ckData)->dwGrandFrames)
\r
485 printf("Frame counts don't match.\n");
\r
489 /* Next scan to the "movi" list. */
\r
492 if (!scanForList(f, NULL, &movi, "movi", "FFFF", false, false, stop))
\r
494 printf("movi LIST was invalid or was not found.\n");
\r
497 if (!readLH(f, &movi)) return false;
\r
499 /* Allocate repCount/offsets array */
\r
500 repCount = (DWORD*)malloc(frameCount*sizeof(DWORD));
\r
503 printf("malloc failure (repCount).\n");
\r
506 memset(repCount, 0, frameCount*sizeof(DWORD));
\r
507 offsets = (DWORD*)malloc(frameCount*sizeof(DWORD));
\r
510 printf("malloc failure (offsets).\n");
\r
513 memset(offsets, 0, frameCount*sizeof(DWORD));
\r
515 /* Seek to "idx1" index and parse info. */
\r
516 if (fseek(f,movi.lstSize-4,SEEK_CUR)) return false;
\r
519 if (!scanForChunk(f, NULL, &idx1, "idx1", false, false))
\r
521 printf("idx1 index chunk was not found or invalid.\n");
\r
524 if (!readChunkHD(f, &idx1, false)) return false;
\r
525 DWORD i = 0, v = 0, ncount = 0, sum = 4;
\r
527 char buf1[5], buf2[5];
\r
528 sprintf(buf1,"%2.2d%s", vstream, "dc");
\r
529 sprintf(buf2,"%2.2d%s", vstream, "db");
\r
532 if (!readIndexChunk(f, &index, sizeof(AVIOLDINDEX))) break;
\r
533 if (index.dwChunkId != MAKE_FOURCC(buf1) && index.dwChunkId != MAKE_FOURCC(buf2))
\r
535 if (index.dwSize == 0) { repCount[v-1]++; ++ncount; }
\r
539 sum += 8+index.dwSize+(index.dwSize&1);
\r
543 printf("\rFrame Parsing Progress: %3.2f%c (%d)",
\r
544 double(i)*100.0/double(frameCount), '%', ncount);
\r
547 printf("\rFrame Parsing Progress: %3.2f%c (%d)\n",
\r
548 double(i)*100.0/double(frameCount), '%', ncount);
\r
549 if (i != frameCount)
\r
551 printf("Number of data chunks does not match frame count.\n");
\r
556 if (newFrameCount == frameCount)
\r
558 printf("Video stream does not contain any NULL frames.\n");
\r
564 bool createTCFile(FILE *tcf, DWORD newFrameCount, double fps, DWORD *repCount, int mode)
\r
566 if (mode == 1 || mode == 3 || mode == 5)
\r
568 fprintf(tcf, "# timecode format v1\n");
\r
570 bool tfr = int(fps*1000000+0.5f) == 119880120 ? true : false;
\r
574 fprintf(tcf, "Assume 29.970030\n");
\r
579 fprintf(tcf, "Assume 29.970000\n");
\r
582 int crep, prep = -20, last = 0;
\r
584 while (i < newFrameCount)
\r
586 crep = repCount[i];
\r
587 if (crep != prep && prep != -20)
\r
589 if (int(rate*1000000+0.5f) != mfps)
\r
590 fprintf(tcf, "%d,%d,%4.6f\n", last, i-1, rate);
\r
593 rate = fps/double(repCount[i]+1);
\r
597 if (int(rate*1000000+0.5f) != mfps)
\r
598 fprintf(tcf, "%d,%d,%4.6f\n", last, newFrameCount-1, rate);
\r
599 fprintf(tcf, "# Total Frames: %d\n", newFrameCount);
\r
601 else if (mode == 2 || mode == 4 || mode == 6)
\r
603 fprintf(tcf, "# timecode format v2\n");
\r
604 DWORD i = 0, sum = 0;
\r
605 while (i < newFrameCount)
\r
607 fprintf(tcf, "%3.6f\n", double(sum)*1000.0/fps);
\r
608 sum += repCount[i]+1;
\r
614 printf("Invalid mode value.\n");
\r
619 printf("Timecode file created successfully.\n");
\r
623 bool createAVIFile(FILE *f, FILE *o, DWORD newFrameCount, DWORD frameCount, DWORD *offsets,
\r
624 DWORD nmovi_size, DWORD nhdrl_size)
\r
628 /* Reset file pointers to beginning. */
\r
629 if (fseek(f,0,SEEK_SET)) return false;
\r
630 if (fseek(o,0,SEEK_SET)) return false;
\r
632 /* Check for valid RIFF header. */
\r
634 if (!checkRiffHeader(f, o, &rh, true))
\r
636 printf("Invalid RIFF header (not an avi file).\n");
\r
640 /* Scan until first "LIST" fourcc. The first one should be "hdrl" type. */
\r
642 if (!scanForList(f, o, &hdrl, "hdrl", "FFFF", true, true, stop))
\r
644 printf("First LIST is invalid.\n");
\r
647 if (!readLH(f, &hdrl)) return false;
\r
648 hdrl.lstSize = nhdrl_size;
\r
649 if (!writeLH(o, &hdrl)) return false;
\r
651 /* Next should be the "avih" chunk with the main AVI Header. */
\r
652 if (!readLHChunk(f, &hdrl, "avih", sizeof(AVIMAINHEADER)))
\r
654 printf("Error reading avih CHUNK.\n");
\r
657 AVIMAINHEADER* am = (AVIMAINHEADER*)hdrl.chkHd->ckData;
\r
658 DWORD streamCount = am->dwStreams;
\r
659 am->dwMicroSecPerFrame = 33367;
\r
661 am->dwTotalFrames = newFrameCount;
\r
662 if (!writeLHChunk(o, &hdrl)) return false;
\r
664 /* Next should be an "strl" list for each stream. */
\r
666 int count = 0, vstream = -20;
\r
668 for (DWORD j=0; j<streamCount; ++j)
\r
671 if (!scanForList(f, o, &tstrl, "strl", "FFFF", true, false, stop))
\r
673 printf("strl LIST is invalid or was not found.\n");
\r
676 if (!readLH(f, &tstrl)) return false;
\r
677 /* Next should be the "strh" chunk which is an AVISTREAMHEADER structure. */
\r
678 if (!readLHChunk(f, &tstrl, "strh", sizeof(AVISTREAMHEADER)))
\r
680 printf("Error reading strh CHUNK.\n");
\r
683 /* See if it is a video stream. */
\r
684 if (((AVISTREAMHEADER*)tstrl.chkHd->ckData)->fccType == MAKE_FOURCC("vids"))
\r
690 printf("More than 1 video stream present.\n");
\r
694 tsize = strl.lstSize-12-sizeof(AVISTREAMHEADER);
\r
695 if (tbuf.size <= tsize+12)
\r
697 if (tbuf.data) free(tbuf.data);
\r
698 tbuf.data = malloc(tsize+12);
\r
699 if (!tbuf.data) return false;
\r
700 tbuf.size = tsize+12;
\r
702 if (!fread(tbuf.data,tsize,1,f)) return false;
\r
706 if (fseek(f,tstrl.lstSize-12-sizeof(AVISTREAMHEADER),SEEK_CUR))
\r
712 printf("No video stream present.\n");
\r
715 AVISTREAMHEADER* ash = (AVISTREAMHEADER*)strl.chkHd->ckData;
\r
716 ash->dwLength = newFrameCount;
\r
717 ash->dwRate = 30000;
\r
718 ash->dwScale = 1001;
\r
719 if (!writeLH(o, &strl)) return false;
\r
720 if (!writeLHChunk(o, &strl)) return false;
\r
721 if (!fwrite(tbuf.data,tsize,1,o)) return false;
\r
724 /* Next grab the AVI2 "odml" LIST. */
\r
726 if (!scanForList(f, o, &odml, "odml", "movi", false, true, stop))
\r
728 if (stop) { goto noodml; }
\r
729 printf("odml LIST was invalid or was not found.\n");
\r
732 if (!readLH(f, &odml)) return false;
\r
733 if (!writeLH(o, &odml)) return false;
\r
735 /* Now should be the "dmlh" CHUNK. */
\r
736 if (!readLHChunk(f, &odml, "dmlh", sizeof(AVIEXTHEADER)))
\r
738 printf("Error reading dmlh CHUNK.\n");
\r
741 AVIEXTHEADER *aeh = (AVIEXTHEADER*)odml.chkHd->ckData;
\r
742 aeh->dwGrandFrames = newFrameCount;
\r
743 if (!writeLHChunk(o, &odml)) return false;
\r
745 /* Next scan to the "movi" list. */
\r
748 if (!scanForList(f, o, &movi, "movi", "FFFF", false, true, stop))
\r
750 printf("movi LIST was invalid or was not found.\n");
\r
753 if (!readLH(f, &movi)) return false;
\r
754 movi.lstSize = nmovi_size;
\r
755 if (!writeLH(o, &movi)) return false;
\r
757 /* Read all data chunks in the stream and count drop frames. */
\r
759 DWORD i = 0, v = 0, sum = 0;
\r
760 char buf1[5], buf2[5];
\r
761 sprintf(buf1,"%2.2d%s", vstream, "dc");
\r
762 sprintf(buf2,"%2.2d%s", vstream, "db");
\r
763 while (i < frameCount)
\r
765 if (!scanForChunk(f, o, &ch, buf1, false, false)) break;
\r
766 if (!readChunk(f, &ch)) return false;
\r
767 if (ch.ckSize != 0)
\r
769 ch.ckID &= 0xFFFF0000;
\r
770 ch.ckID |= 0x00003030;
\r
771 if (!writeChunk(o, &ch)) return false;
\r
774 printf("\rFrame Modification Progress: %3.2f%c",
\r
775 double(i)*100.0/double(frameCount), '%');
\r
778 printf("\rFrame Modification Progress: %3.2f%c\n",
\r
779 double(i)*100.0/double(frameCount), '%');
\r
780 if (i != frameCount)
\r
782 printf("Number of data chunks does not match frame count.\n");
\r
786 /* Read the "idx1" index and modify the entries as required. */
\r
790 if (!scanForChunk(f, o, &idx1, "idx1", false, true))
\r
792 printf("idx1 index chunk was not found or invalid.\n");
\r
795 if (!readChunkHD(f, &idx1, false)) return false;
\r
796 idx1.ckSize = newFrameCount*sizeof(AVIOLDINDEX);
\r
797 if (!writeChunkHD(o, &idx1)) return false;
\r
800 if (!readIndexChunk(f, &index, sizeof(AVIOLDINDEX))) break;
\r
801 if (index.dwChunkId == MAKE_FOURCC(buf1) || index.dwChunkId == MAKE_FOURCC(buf2))
\r
803 if (index.dwSize == 0) continue;
\r
804 index.dwOffset = offsets[i];
\r
805 index.dwChunkId &= 0xFFFF0000;
\r
806 index.dwChunkId |= 0x00003030;
\r
807 if (!writeIndexChunk(o, &index, sizeof(AVIOLDINDEX))) return false;
\r
809 printf("\rIndex Modification Progress: %3.2f%c",
\r
810 double(i)*100.0/double(newFrameCount), '%');
\r
814 printf("\rIndex Modification Progress: %3.2f%c\n",
\r
815 double(i)*100.0/double(newFrameCount), '%');
\r
816 if (i != newFrameCount)
\r
818 printf("Number of index chunks does not match frame count.\n");
\r
822 /* flush the end bits */
\r
825 DWORD ret = (DWORD)fread(tbuf.data,1,tbuf.size,f);
\r
827 if (!fwrite(tbuf.data,1,ret,o)) return false;
\r
831 /* Modify the size value of the RIFF Header at the
\r
832 beginning of the file. */
\r
833 if (fseek(o, 0, SEEK_SET)) return false;
\r
834 rh.fileSize = wpos-8;
\r
835 if (!fwrite(&rh,1,12,o))
\r
837 printf("Error writing to output file.\n");
\r
843 bool createAVSFile(FILE *avsf, char *filename, DWORD newFrameCount, DWORD *repCount)
\r
845 DWORD *startFrames = (DWORD*)malloc(newFrameCount*sizeof(DWORD));
\r
848 DWORD *endFrames = (DWORD*)malloc(newFrameCount*sizeof(DWORD));
\r
854 DWORD *frameCounts = (DWORD*)malloc(newFrameCount*sizeof(DWORD));
\r
861 memset(startFrames,0,newFrameCount*sizeof(DWORD));
\r
862 memset(endFrames,0,newFrameCount*sizeof(DWORD));
\r
863 memset(frameCounts,0,newFrameCount*sizeof(DWORD));
\r
864 DWORD sectionCount = 0;
\r
865 DWORD frameSum = 0;
\r
867 for (DWORD i=0; i<newFrameCount-1; ++i)
\r
869 if (repCount[i] != repCount[i+1])
\r
871 startFrames[sectionCount] = frameSum;
\r
872 frameCounts[sectionCount] = repCount[i]+1;
\r
873 frameSum += (repCount[i]+1)*(i-last);
\r
874 endFrames[sectionCount] = frameSum-1;
\r
879 startFrames[sectionCount] = frameSum;
\r
880 frameCounts[sectionCount] = repCount[i]+1;
\r
881 frameSum += (repCount[i]+1)*(i-last);
\r
882 endFrames[sectionCount] = frameSum-1;
\r
884 fprintf(avsf, "Avisource(\"%s\")\n", filename);
\r
885 char abuf[4096], tbuf[40];
\r
887 for (DWORD j=0; j<sectionCount; ++j)
\r
889 fprintf(avsf, "cfr%03d = Trim(%d,%d).SelectEvery(%d,0).AssumeFPS(30000,1001)\n", j,
\r
890 startFrames[j], endFrames[j], frameCounts[j]);
\r
891 if (j == 0) sprintf(tbuf,"cfr%03d",j);
\r
892 else sprintf(tbuf,"+cfr%03d",j);
\r
895 fprintf(avsf, "%s\n", abuf);
\r
902 int main(int argc, char *argv[])
\r
904 DWORD frameCount, newFrameCount;
\r
905 DWORD nmovi_size, nhdrl_size;
\r
906 DWORD *repCount = 0, *offsets = 0;
\r
907 clock_t start, finish;
\r
909 char *fbuf = 0, *obuf = 0;
\r
911 printf("\ncfr2tc %s by tritical.\n", VERSION);
\r
913 /* Check for valid syntax. */
\r
916 printf("Invalid syntax used.\n");
\r
917 printf("Usage: cfr2tc input_avi_file output_avi_or_avs_file timecode_file mode\n");
\r
918 printf("Example: cfr2tc c:\\test.avi c:\\video.avi c:\\timecodes.txt 1\n");
\r
919 printf("Mode settings: 1 - v1 timecode file, avi output\n");
\r
920 printf(" 2 - v2 timecode file, avi output\n");
\r
921 printf(" 3 - v1 timecode file, no video output\n");
\r
922 printf(" 4 - v2 timecode file, no video output\n");
\r
923 printf(" 5 - v1 timecode file, avs output\n");
\r
924 printf(" 6 - v2 timecode file, avs output\n");
\r
929 /* Check for valid mode setting. */
\r
930 int mode = atoi(argv[4]);
\r
931 if (mode < 1 || mode > 6)
\r
933 printf("Invalid mode setting (%d).\n", mode);
\r
937 /* Open the specified avi file. */
\r
938 FILE *f = fopen(argv[1],"rb");
\r
941 printf("Error opening input avi file.\n");
\r
944 fbuf = (char*)_aligned_malloc(65536*sizeof(char),32);
\r
947 printf("Error allocating file buffer for input avi file.\n");
\r
948 closeFiles(f, NULL, NULL, repCount, offsets, false, NULL, NULL, fbuf, obuf);
\r
951 if (setvbuf(f,fbuf,_IOFBF,65536))
\r
953 printf("Error setting input avi file buffer.\n");
\r
954 closeFiles(f, NULL, NULL, repCount, offsets, false, NULL, NULL, fbuf, obuf);
\r
958 /* Open the output avi file. */
\r
962 o = fopen(argv[2],"wb");
\r
965 printf("Error opening output avi file.\n");
\r
966 closeFiles(f, NULL, NULL, repCount, offsets, false, NULL, NULL, fbuf, obuf);
\r
969 obuf = (char*)_aligned_malloc(65536*sizeof(char),32);
\r
972 printf("Error allocating file buffer for output avi file.\n");
\r
973 closeFiles(f, o, NULL, repCount, offsets, true, argv[2], NULL, fbuf, obuf);
\r
976 if (setvbuf(o,obuf,_IOFBF,65536))
\r
978 printf("Error setting output avi file buffer.\n");
\r
979 closeFiles(f, o, NULL, repCount, offsets, true, argv[2], NULL, fbuf, obuf);
\r
984 /* Open the timecode file. */
\r
985 FILE *tcf = fopen(argv[3],"w");
\r
988 printf("Error opening timecode file.\n");
\r
989 closeFiles(f, o, NULL, repCount, offsets, true, argv[2], NULL, fbuf, obuf);
\r
993 /* Find fps and number of null frames. */
\r
994 if (!getInfo(f, frameCount, newFrameCount, fps, repCount, offsets, nmovi_size, nhdrl_size))
\r
996 closeFiles(f, o, tcf, repCount, offsets, true, argv[2], argv[3], fbuf, obuf);
\r
1000 /* Create the timecode file. */
\r
1001 if (!createTCFile(tcf, newFrameCount, fps, repCount, mode))
\r
1003 closeFiles(f, o, tcf, repCount, offsets, true, argv[2], argv[3], fbuf, obuf);
\r
1007 /* Create the output avi file with NULL frames removed. */
\r
1010 if (!createAVIFile(f, o, newFrameCount, frameCount, offsets, nmovi_size, nhdrl_size))
\r
1012 closeFiles(f, o, tcf, repCount, offsets, true, argv[2], argv[3], fbuf, obuf);
\r
1017 /* Create avs script */
\r
1020 FILE *avsf = fopen(argv[2],"w");
\r
1023 printf("Error creating avs file.\n");
\r
1024 closeFiles(f, o, tcf, repCount, offsets, true, argv[2], argv[3], fbuf, obuf);
\r
1027 if (!createAVSFile(avsf, argv[1], newFrameCount, repCount))
\r
1031 closeFiles(f, o, tcf, repCount, offsets, true, argv[2], argv[3], fbuf, obuf);
\r
1037 /* Cleanup and finish. */
\r
1039 printf("All processing completed successfully.\n");
\r
1040 printf("Time elapsed: %3.3f seconds.\n", (double)(finish-start)/CLOCKS_PER_SEC);
\r
1041 closeFiles(f, o, tcf, repCount, offsets, false, NULL, NULL, fbuf, obuf);
\r