OSDN Git Service

add tstools.
[rec10/rec10-git.git] / tstools / cfr2tc / cfr2tc / cfr2tc.cpp
1 /*\r
2 **                   cfr2tc v1.4 - July 26, 2006\r
3 **\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
7 **\r
8 **   Copyright (C) 2005-2006 Kevin Stone\r
9 **\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
14 **\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
19 **\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
23 */\r
24 \r
25 #include <windows.h>\r
26 #include <stdio.h>\r
27 #include <time.h>\r
28 #include <malloc.h>\r
29 \r
30 #define VERSION "v1.4"\r
31 \r
32 typedef DWORD FOURCC;\r
33 \r
34 typedef struct\r
35 {\r
36         FOURCC riff;\r
37         DWORD fileSize;\r
38         DWORD fileType;\r
39 } RIFFHEADER;\r
40 \r
41 class CHUNKHEADER\r
42 {\r
43 public:\r
44         FOURCC ckID;\r
45         DWORD ckSize;\r
46         DWORD ckDataSize;\r
47         void *ckData;\r
48         CHUNKHEADER::CHUNKHEADER() : ckID(0), ckSize(0), ckData(0), ckDataSize(0) {};\r
49         CHUNKHEADER::~CHUNKHEADER() { if (ckData) free(ckData); }\r
50 };\r
51 \r
52 class LISTHEADER\r
53 {\r
54 public:\r
55         FOURCC lstID;\r
56         DWORD lstSize;\r
57         FOURCC lstType;\r
58         CHUNKHEADER *chkHd;\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
62         {\r
63                 lstID = ob2.lstID;\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
74                 return *this;\r
75         }\r
76 };\r
77 \r
78 class TBUF\r
79 {\r
80 public:\r
81         DWORD size;\r
82         void *data;\r
83         TBUF::TBUF() : data(0), size(0) {};\r
84         TBUF::~TBUF() { if (data) free(data); }\r
85 };\r
86 \r
87 typedef struct \r
88 {\r
89         FOURCC fccType;\r
90         FOURCC fccHandler;\r
91         DWORD  dwFlags;\r
92         WORD   wPriority;\r
93         WORD   wLanguage;\r
94         DWORD  dwInitialFrames;\r
95         DWORD  dwScale;\r
96         DWORD  dwRate;\r
97         DWORD  dwStart;\r
98         DWORD  dwLength;\r
99         DWORD  dwSuggestedBufferSize;\r
100         DWORD  dwQuality;\r
101         DWORD  dwSampleSize;\r
102         struct {\r
103                 short int left;\r
104                 short int top;\r
105                 short int right;\r
106                 short int bottom;\r
107         }  rcFrame;\r
108 } AVISTREAMHEADER;\r
109 \r
110 typedef struct \r
111 {\r
112         DWORD  dwMicroSecPerFrame;\r
113         DWORD  dwMaxBytesPerSec;\r
114         DWORD  dwPaddingGranularity;\r
115         DWORD  dwFlags;\r
116         DWORD  dwTotalFrames;\r
117         DWORD  dwInitialFrames;\r
118         DWORD  dwStreams;\r
119         DWORD  dwSuggestedBufferSize;\r
120         DWORD  dwWidth;\r
121         DWORD  dwHeight;\r
122         DWORD  dwReserved[4];\r
123 } AVIMAINHEADER;\r
124 \r
125 typedef struct \r
126 {\r
127         DWORD   dwGrandFrames;\r
128         DWORD   dwFuture[61];\r
129 } AVIEXTHEADER;\r
130 \r
131 typedef struct\r
132 {\r
133         DWORD   dwChunkId;\r
134         DWORD   dwFlags;\r
135         DWORD   dwOffset;\r
136         DWORD   dwSize;\r
137 } AVIOLDINDEX;\r
138 \r
139 // globals\r
140 TBUF tbuf;\r
141 DWORD wpos = 0;\r
142 \r
143 DWORD MAKE_FOURCC(char* fourCC)\r
144 {\r
145         return (fourCC[0]|(fourCC[1]<<8)|(fourCC[2]<<16)|(fourCC[3]<<24));\r
146 }\r
147 \r
148 bool checkRiffHeader(FILE *f, FILE *o, RIFFHEADER *rh, bool write)\r
149 {\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
154         return true;\r
155 }\r
156 \r
157 bool scanForList(FILE *f, FILE *o, LISTHEADER *lh, char *fourCC, char *fourCCStop,\r
158                                  bool force, bool write, bool &stop)\r
159 {\r
160         lh->lstID = 0;\r
161         stop = false;\r
162 repeat:\r
163         if (!fread(&lh->lstID,1,2,f)) return false;\r
164         while(1)\r
165         {\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
170         }\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
174         {\r
175                 if (force) return false;\r
176                 if (lh->lstType == MAKE_FOURCC(fourCCStop))\r
177                 {\r
178                         if (fseek(f,-12,SEEK_CUR)) return false;\r
179                         stop = true;\r
180                         return false;\r
181                 }\r
182                 DWORD fsize = lh->lstSize-4-(lh->lstSize&1);\r
183                 if (write)\r
184                 {\r
185                         if (tbuf.size <= fsize+12)\r
186                         {\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
191                         }\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
197                         wpos += 12+fsize;\r
198                 }\r
199                 else\r
200                 {\r
201                         if (fseek(f,fsize,SEEK_CUR)) \r
202                                 return false;\r
203                 }\r
204                 goto repeat;\r
205         }\r
206         if (fseek(f,-12,SEEK_CUR)) return false;\r
207         return true;\r
208 }\r
209 \r
210 bool readLH(FILE *f, LISTHEADER *lh)\r
211 {\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
215         return true;\r
216 }\r
217 \r
218 bool writeLH(FILE *o, LISTHEADER *lh)\r
219 {\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
223         wpos += 12;\r
224         return true;\r
225 }\r
226 \r
227 bool readLHChunk(FILE *f, LISTHEADER *lh, char *fourCC, DWORD size)\r
228 {\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
241         return true;\r
242 }\r
243 \r
244 bool writeLHChunk(FILE *o, LISTHEADER *lh)\r
245 {\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
250         return true;\r
251 }\r
252 \r
253 bool scanForChunk(FILE *f, FILE *o, CHUNKHEADER *ch, char *fourCC, bool force, bool write)\r
254 {\r
255 repeat:\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
261         {\r
262                 if (fseek(f, -8, SEEK_CUR)) return false;\r
263                 return true;\r
264         }\r
265         else\r
266         {\r
267                 if (force) return false;\r
268                 DWORD pad_size = ch->ckSize+(ch->ckSize&1);\r
269                 if (write) \r
270                 {\r
271                         if (tbuf.size <= pad_size+12)\r
272                         {\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
277                         }\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
283                 }\r
284                 else\r
285                 {\r
286                         if (fseek(f,pad_size,SEEK_CUR))\r
287                                 return false;\r
288                 }\r
289                 goto repeat;\r
290         }\r
291         return false;\r
292 }\r
293 \r
294 bool readChunk(FILE *f, CHUNKHEADER *ch)\r
295 {\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
300         {\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
305         }\r
306         if (!fread(ch->ckData,pad_size,1,f) && pad_size != 0) return false;\r
307         return true;\r
308 }\r
309 \r
310 bool readChunkHD(FILE *f, CHUNKHEADER *ch, bool seek)\r
311 {\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
315         return true;\r
316 }\r
317 \r
318 bool writeChunk(FILE *o, CHUNKHEADER *ch)\r
319 {\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
325         return true;\r
326 }\r
327 \r
328 bool writeChunkHD(FILE *o, CHUNKHEADER *ch)\r
329 {\r
330         if (!fwrite(&ch->ckID,4,1,o)) return false;\r
331         if (!fwrite(&ch->ckSize,4,1,o)) return false;\r
332         wpos += 8;\r
333         return true;\r
334 }\r
335 \r
336 bool readIndexChunk(FILE *f, AVIOLDINDEX *index, int size)\r
337 {\r
338         if (!fread(index,size,1,f)) return false;\r
339         return true;\r
340 }\r
341 \r
342 bool writeIndexChunk(FILE *o, AVIOLDINDEX *index, int size)\r
343 {\r
344         if (!fwrite(index,size,1,o)) return false;\r
345         wpos += size;\r
346         return true;\r
347 }\r
348 \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
351 {\r
352         if (f) fclose(f);\r
353         if (o) \r
354         {\r
355                 fclose(o);\r
356                 if (ul && oname) unlink(oname);\r
357         }\r
358         if (tcf) \r
359         {\r
360                 fclose(tcf);\r
361                 if (ul && tcfname) unlink(tcfname);\r
362         }\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
367 }\r
368 \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
371 {\r
372         bool stop;\r
373 \r
374         /* Check for valid RIFF header. */\r
375         RIFFHEADER rh;\r
376         if (!checkRiffHeader(f, NULL, &rh, false))\r
377         {\r
378                 printf("Invalid RIFF header (not an avi file).\n");\r
379                 return false;\r
380         }\r
381 \r
382         /* Scan until first "LIST" fourcc.  The first one should be "hdrl" type. */\r
383         LISTHEADER hdrl;\r
384         if (!scanForList(f, NULL, &hdrl, "hdrl", "FFFF", true, false, stop))\r
385         {\r
386                 printf("First LIST is invalid.\n");\r
387                 return false;\r
388         }\r
389         if (!readLH(f, &hdrl)) return false;\r
390         nhdrl_size = hdrl.lstSize;\r
391 \r
392         /* Next should be the "avih" chunk with the main AVI Header. */\r
393         if (!readLHChunk(f, &hdrl, "avih", sizeof(AVIMAINHEADER)))\r
394         {\r
395                 printf("Error reading avih CHUNK.\n");\r
396                 return false;\r
397         }\r
398         AVIMAINHEADER* am = (AVIMAINHEADER*)hdrl.chkHd->ckData;\r
399         frameCount = am->dwTotalFrames;\r
400         DWORD streamCount = am->dwStreams;\r
401 \r
402         /* Make sure that the file has an "idx1" index. */\r
403         if (!(am->dwFlags&0x00000010))\r
404         {\r
405                 printf("File has no \"idx1\" index (dwFlags).\n");\r
406                 return false;\r
407         }\r
408 \r
409         /* Make sure that AVIF_MUSTUSEINDEX is not set. */\r
410         if (am->dwFlags&0x00000020)\r
411         {\r
412                 printf("Cannot use a file with AVIF_MUSTUSEINDEX flag set.\n");\r
413                 return false;\r
414         }\r
415 \r
416         /* Next should be an "strl" list for each stream. */\r
417         LISTHEADER strl;\r
418         int count = 0, vstream = -20;\r
419         for (DWORD j=0; j<streamCount; ++j)\r
420         {\r
421                 LISTHEADER tstrl;\r
422                 if (!scanForList(f, NULL, &tstrl, "strl", "FFFF", true, false, stop))\r
423                 {\r
424                         printf("strl LIST is invalid or was not found.\n");\r
425                         return false;\r
426                 }\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
430                 {\r
431                         printf("Error reading strh CHUNK.\n");\r
432                         return false;\r
433                 }\r
434                 /* See if it is a video stream. */\r
435                 if (((AVISTREAMHEADER*)tstrl.chkHd->ckData)->fccType == MAKE_FOURCC("vids"))\r
436                 {\r
437                         ++count;\r
438                         vstream = j;\r
439                         if (count > 1)\r
440                         {\r
441                                 printf("More than 1 video stream present.\n");\r
442                                 return false;\r
443                         }\r
444                         strl = tstrl;\r
445                 }\r
446                 else nhdrl_size -= 8+tstrl.lstSize;\r
447                 if (fseek(f,tstrl.lstSize-12-sizeof(AVISTREAMHEADER),SEEK_CUR)) \r
448                         return false;\r
449         }\r
450         if (!count)\r
451         {\r
452                 printf("No video stream present.\n");\r
453                 return false;\r
454         }\r
455         AVISTREAMHEADER* ash = (AVISTREAMHEADER*)strl.chkHd->ckData;\r
456         fps = double(ash->dwRate)/double(ash->dwScale);\r
457         \r
458         /* Make sure that AVISF_VIDEO_PALCHANGES is not set. */\r
459         if (ash->dwFlags&0x00010000)\r
460         {\r
461                 printf("Cannot use a stream with AVISF_VIDEO_PALCHANGES flag set.\n");\r
462                 return false;\r
463         }\r
464 \r
465         /* Next grab the AVI2 "odml" LIST. */\r
466         LISTHEADER odml;\r
467         if (!scanForList(f, NULL, &odml, "odml", "movi", false, false, stop))\r
468         {\r
469                 if (stop) { goto noodml; }\r
470                 printf("odml LIST was invalid or was not found.\n");\r
471                 return false;\r
472         }\r
473         if (!readLH(f, &odml)) return false;\r
474 \r
475         /* Now should be the "dmlh" CHUNK. */\r
476         if (!readLHChunk(f, &odml, "dmlh", sizeof(AVIEXTHEADER)))\r
477         {\r
478                 printf("Error reading dmlh CHUNK.\n");\r
479                 return false;\r
480         }\r
481 \r
482         /* Make sure frame counts match. */\r
483         if (frameCount != ((AVIEXTHEADER*)odml.chkHd->ckData)->dwGrandFrames)\r
484         {\r
485                 printf("Frame counts don't match.\n");\r
486                 return false;\r
487         }\r
488 \r
489         /* Next scan to the "movi" list. */\r
490 noodml:\r
491         LISTHEADER movi;\r
492         if (!scanForList(f, NULL, &movi, "movi", "FFFF", false, false, stop))\r
493         {\r
494                 printf("movi LIST was invalid or was not found.\n");\r
495                 return false;\r
496         }\r
497         if (!readLH(f, &movi)) return false;\r
498 \r
499         /* Allocate repCount/offsets array */\r
500         repCount = (DWORD*)malloc(frameCount*sizeof(DWORD));\r
501         if (!repCount)\r
502         {\r
503                 printf("malloc failure (repCount).\n");\r
504                 return false;\r
505         }\r
506         memset(repCount, 0, frameCount*sizeof(DWORD));\r
507         offsets = (DWORD*)malloc(frameCount*sizeof(DWORD));\r
508         if (!offsets)\r
509         {\r
510                 printf("malloc failure (offsets).\n");\r
511                 return false;\r
512         }\r
513         memset(offsets, 0, frameCount*sizeof(DWORD));\r
514 \r
515         /* Seek to "idx1" index and parse info. */\r
516         if (fseek(f,movi.lstSize-4,SEEK_CUR)) return false;\r
517         CHUNKHEADER idx1;\r
518         AVIOLDINDEX index;\r
519         if (!scanForChunk(f, NULL, &idx1, "idx1", false, false))\r
520         {\r
521                 printf("idx1 index chunk was not found or invalid.\n");\r
522                 return false;\r
523         }\r
524         if (!readChunkHD(f, &idx1, false)) return false;\r
525         DWORD i = 0, v = 0, ncount = 0, sum = 4;\r
526         nmovi_size = 0;\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
530         while(1)\r
531         {\r
532                 if (!readIndexChunk(f, &index, sizeof(AVIOLDINDEX))) break;\r
533                 if (index.dwChunkId != MAKE_FOURCC(buf1) && index.dwChunkId != MAKE_FOURCC(buf2)) \r
534                         continue;\r
535                 if (index.dwSize == 0) { repCount[v-1]++; ++ncount; }\r
536                 else \r
537                 {\r
538                         offsets[v] = sum;\r
539                         sum += 8+index.dwSize+(index.dwSize&1);\r
540                         ++v;\r
541                 }\r
542                 if (!(i&15))\r
543                         printf("\rFrame Parsing Progress: %3.2f%c (%d)", \r
544                                 double(i)*100.0/double(frameCount), '%', ncount); \r
545                 ++i;\r
546         }\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
550         {\r
551                 printf("Number of data chunks does not match frame count.\n");\r
552                 return false;\r
553         }\r
554         nmovi_size = sum;\r
555         newFrameCount = v;\r
556         if (newFrameCount == frameCount)\r
557         {\r
558                 printf("Video stream does not contain any NULL frames.\n");\r
559                 return false;\r
560         }\r
561         return true;\r
562 }\r
563 \r
564 bool createTCFile(FILE *tcf, DWORD newFrameCount, double fps, DWORD *repCount, int mode)\r
565 {\r
566         if (mode == 1 || mode == 3 || mode == 5)\r
567         {\r
568                 fprintf(tcf, "# timecode format v1\n");\r
569                 int mfps;\r
570                 bool tfr = int(fps*1000000+0.5f) == 119880120 ? true : false; \r
571                 if (tfr) \r
572                 {\r
573                         mfps = 29970030;\r
574                         fprintf(tcf, "Assume 29.970030\n");\r
575                 }\r
576                 else \r
577                 {\r
578                         mfps = 29970000;\r
579                         fprintf(tcf, "Assume 29.970000\n");\r
580                 }\r
581                 DWORD i = 0;\r
582                 int crep, prep = -20, last = 0;\r
583                 double rate;\r
584                 while (i < newFrameCount)\r
585                 {\r
586                         crep = repCount[i];\r
587                         if (crep != prep && prep != -20)\r
588                         {\r
589                                 if (int(rate*1000000+0.5f) != mfps)\r
590                                         fprintf(tcf, "%d,%d,%4.6f\n", last, i-1, rate);\r
591                                 last = i;\r
592                         }\r
593                         rate = fps/double(repCount[i]+1);\r
594                         prep = crep;\r
595                         ++i;\r
596                 }\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
600         }\r
601         else if (mode == 2 || mode == 4 || mode == 6)\r
602         {\r
603                 fprintf(tcf, "# timecode format v2\n");\r
604                 DWORD i = 0, sum = 0;\r
605                 while (i < newFrameCount)\r
606                 {\r
607                         fprintf(tcf, "%3.6f\n", double(sum)*1000.0/fps);\r
608                         sum += repCount[i]+1;\r
609                         ++i;\r
610                 }\r
611         }\r
612         else\r
613         {\r
614                 printf("Invalid mode value.\n");\r
615                 return false;\r
616         }\r
617         fclose(tcf);\r
618         tcf = NULL;\r
619         printf("Timecode file created successfully.\n");\r
620         return true;\r
621 }\r
622 \r
623 bool createAVIFile(FILE *f, FILE *o, DWORD newFrameCount, DWORD frameCount, DWORD *offsets,\r
624                                    DWORD nmovi_size, DWORD nhdrl_size)\r
625 {\r
626         bool stop;\r
627 \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
631 \r
632         /* Check for valid RIFF header. */\r
633         RIFFHEADER rh;\r
634         if (!checkRiffHeader(f, o, &rh, true))\r
635         {\r
636                 printf("Invalid RIFF header (not an avi file).\n");\r
637                 return false;\r
638         }\r
639 \r
640         /* Scan until first "LIST" fourcc.  The first one should be "hdrl" type. */\r
641         LISTHEADER hdrl;\r
642         if (!scanForList(f, o, &hdrl, "hdrl", "FFFF", true, true, stop))\r
643         {\r
644                 printf("First LIST is invalid.\n");\r
645                 return false;\r
646         }\r
647         if (!readLH(f, &hdrl)) return false;\r
648         hdrl.lstSize = nhdrl_size;\r
649         if (!writeLH(o, &hdrl)) return false;\r
650 \r
651         /* Next should be the "avih" chunk with the main AVI Header. */\r
652         if (!readLHChunk(f, &hdrl, "avih", sizeof(AVIMAINHEADER)))\r
653         {\r
654                 printf("Error reading avih CHUNK.\n");\r
655                 return false;\r
656         }\r
657         AVIMAINHEADER* am = (AVIMAINHEADER*)hdrl.chkHd->ckData;\r
658         DWORD streamCount = am->dwStreams;\r
659         am->dwMicroSecPerFrame = 33367;\r
660         am->dwStreams = 1;\r
661         am->dwTotalFrames = newFrameCount;\r
662         if (!writeLHChunk(o, &hdrl)) return false;\r
663 \r
664         /* Next should be an "strl" list for each stream. */\r
665         LISTHEADER strl;\r
666         int count = 0, vstream = -20;\r
667         DWORD tsize = 0;\r
668         for (DWORD j=0; j<streamCount; ++j)\r
669         {\r
670                 LISTHEADER tstrl;\r
671                 if (!scanForList(f, o, &tstrl, "strl", "FFFF", true, false, stop))\r
672                 {\r
673                         printf("strl LIST is invalid or was not found.\n");\r
674                         return false;\r
675                 }\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
679                 {\r
680                         printf("Error reading strh CHUNK.\n");\r
681                         return false;\r
682                 }\r
683                 /* See if it is a video stream. */\r
684                 if (((AVISTREAMHEADER*)tstrl.chkHd->ckData)->fccType == MAKE_FOURCC("vids"))\r
685                 {\r
686                         ++count;\r
687                         vstream = j;\r
688                         if (count > 1)\r
689                         {\r
690                                 printf("More than 1 video stream present.\n");\r
691                                 return false;\r
692                         }\r
693                         strl = tstrl;\r
694                         tsize = strl.lstSize-12-sizeof(AVISTREAMHEADER);\r
695                         if (tbuf.size <= tsize+12)\r
696                         {\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
701                         }\r
702                         if (!fread(tbuf.data,tsize,1,f)) return false;\r
703                 }\r
704                 else\r
705                 {\r
706                         if (fseek(f,tstrl.lstSize-12-sizeof(AVISTREAMHEADER),SEEK_CUR)) \r
707                                 return false;\r
708                 }\r
709         }\r
710         if (!count)\r
711         {\r
712                 printf("No video stream present.\n");\r
713                 return false;\r
714         }\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
722         wpos += tsize;\r
723 \r
724         /* Next grab the AVI2 "odml" LIST. */\r
725         LISTHEADER odml;\r
726         if (!scanForList(f, o, &odml, "odml", "movi", false, true, stop))\r
727         {\r
728                 if (stop) { goto noodml; }\r
729                 printf("odml LIST was invalid or was not found.\n");\r
730                 return false;\r
731         }\r
732         if (!readLH(f, &odml)) return false;\r
733         if (!writeLH(o, &odml)) return false;\r
734 \r
735         /* Now should be the "dmlh" CHUNK. */\r
736         if (!readLHChunk(f, &odml, "dmlh", sizeof(AVIEXTHEADER)))\r
737         {\r
738                 printf("Error reading dmlh CHUNK.\n");\r
739                 return false;\r
740         }\r
741         AVIEXTHEADER *aeh = (AVIEXTHEADER*)odml.chkHd->ckData;\r
742         aeh->dwGrandFrames = newFrameCount;\r
743         if (!writeLHChunk(o, &odml)) return false;\r
744 \r
745         /* Next scan to the "movi" list. */\r
746 noodml:\r
747         LISTHEADER movi;\r
748         if (!scanForList(f, o, &movi, "movi", "FFFF", false, true, stop))\r
749         {\r
750                 printf("movi LIST was invalid or was not found.\n");\r
751                 return false;\r
752         }\r
753         if (!readLH(f, &movi)) return false;\r
754         movi.lstSize = nmovi_size;\r
755         if (!writeLH(o, &movi)) return false;\r
756 \r
757         /* Read all data chunks in the stream and count drop frames. */\r
758         CHUNKHEADER ch;\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
764         {\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
768                 {\r
769                         ch.ckID &= 0xFFFF0000;\r
770                         ch.ckID |= 0x00003030;\r
771                         if (!writeChunk(o, &ch)) return false; \r
772                 }\r
773                 if (!(i&15))\r
774                         printf("\rFrame Modification Progress: %3.2f%c", \r
775                                 double(i)*100.0/double(frameCount), '%'); \r
776                 ++i;\r
777         }\r
778         printf("\rFrame Modification Progress: %3.2f%c\n", \r
779                 double(i)*100.0/double(frameCount), '%');\r
780         if (i != frameCount)\r
781         {\r
782                 printf("Number of data chunks does not match frame count.\n");\r
783                 return false;\r
784         }\r
785 \r
786         /* Read the "idx1" index and modify the entries as required. */\r
787         i = 0, sum = 0;\r
788         CHUNKHEADER idx1;\r
789         AVIOLDINDEX index;\r
790         if (!scanForChunk(f, o, &idx1, "idx1", false, true))\r
791         {\r
792                 printf("idx1 index chunk was not found or invalid.\n");\r
793                 return false;\r
794         }\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
798         while(1)\r
799         {\r
800                 if (!readIndexChunk(f, &index, sizeof(AVIOLDINDEX))) break;\r
801                 if (index.dwChunkId == MAKE_FOURCC(buf1) || index.dwChunkId == MAKE_FOURCC(buf2)) \r
802                 {\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
808                         if (!(i&15))\r
809                                 printf("\rIndex Modification Progress: %3.2f%c", \r
810                                         double(i)*100.0/double(newFrameCount), '%');\r
811                         ++i;\r
812                 }\r
813         }\r
814         printf("\rIndex Modification Progress: %3.2f%c\n", \r
815                 double(i)*100.0/double(newFrameCount), '%');\r
816         if (i != newFrameCount)\r
817         {\r
818                 printf("Number of index chunks does not match frame count.\n");\r
819                 return false;\r
820         }\r
821 \r
822         /* flush the end bits */\r
823         while (1)\r
824         {\r
825                 DWORD ret = (DWORD)fread(tbuf.data,1,tbuf.size,f);\r
826                 if (!ret) break;\r
827                 if (!fwrite(tbuf.data,1,ret,o)) return false;\r
828                 wpos += ret;\r
829         }\r
830 \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
836         {\r
837                 printf("Error writing to output file.\n");\r
838                 return false;\r
839         }\r
840         return true;\r
841 }\r
842 \r
843 bool createAVSFile(FILE *avsf, char *filename, DWORD newFrameCount, DWORD *repCount)\r
844 {\r
845         DWORD *startFrames = (DWORD*)malloc(newFrameCount*sizeof(DWORD));\r
846         if (!startFrames)\r
847                 return false;\r
848         DWORD *endFrames = (DWORD*)malloc(newFrameCount*sizeof(DWORD));\r
849         if (!endFrames)\r
850         {\r
851                 free(startFrames);\r
852                 return false;\r
853         }\r
854         DWORD *frameCounts = (DWORD*)malloc(newFrameCount*sizeof(DWORD));\r
855         if (!frameCounts)\r
856         {\r
857                 free(startFrames);\r
858                 free(endFrames);\r
859                 return false;\r
860         }\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
866         long last = -1;\r
867         for (DWORD i=0; i<newFrameCount-1; ++i)\r
868         {\r
869                 if (repCount[i] != repCount[i+1])\r
870                 {\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
875                         last = i;\r
876                         ++sectionCount;\r
877                 }\r
878         }\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
883         ++sectionCount;\r
884         fprintf(avsf, "Avisource(\"%s\")\n", filename);\r
885         char abuf[4096], tbuf[40];\r
886         sprintf(abuf,"");\r
887         for (DWORD j=0; j<sectionCount; ++j)\r
888         {\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
893                 strcat(abuf,tbuf);\r
894         }\r
895         fprintf(avsf, "%s\n", abuf);\r
896         free(startFrames);\r
897         free(endFrames);\r
898         free(frameCounts);\r
899         return true;\r
900 }\r
901 \r
902 int main(int argc, char *argv[])\r
903 {\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
908         double fps;\r
909         char *fbuf = 0, *obuf = 0;\r
910 \r
911         printf("\ncfr2tc %s by tritical.\n", VERSION);\r
912 \r
913         /* Check for valid syntax. */\r
914         if (argc != 5)\r
915         {\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
925                 return 1;\r
926         }\r
927         start = clock();\r
928 \r
929         /* Check for valid mode setting. */\r
930         int mode = atoi(argv[4]);\r
931         if (mode < 1 || mode > 6)\r
932         {\r
933                 printf("Invalid mode setting (%d).\n", mode);\r
934                 return 2;\r
935         }\r
936 \r
937         /* Open the specified avi file. */\r
938         FILE *f = fopen(argv[1],"rb");\r
939         if (!f)\r
940         {\r
941                 printf("Error opening input avi file.\n");\r
942                 return 3;\r
943         }\r
944         fbuf = (char*)_aligned_malloc(65536*sizeof(char),32);\r
945         if (!fbuf)\r
946         {\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
949                 return 4;\r
950         }\r
951         if (setvbuf(f,fbuf,_IOFBF,65536))\r
952         {\r
953                 printf("Error setting input avi file buffer.\n");\r
954                 closeFiles(f, NULL, NULL, repCount, offsets, false, NULL, NULL, fbuf, obuf);\r
955                 return 5;\r
956         }\r
957 \r
958         /* Open the output avi file. */\r
959         FILE *o = NULL;\r
960         if (mode < 3)\r
961         {\r
962                 o = fopen(argv[2],"wb");\r
963                 if (!o)\r
964                 {\r
965                         printf("Error opening output avi file.\n");\r
966                         closeFiles(f, NULL, NULL, repCount, offsets, false, NULL, NULL, fbuf, obuf);\r
967                         return 6;\r
968                 }\r
969                 obuf = (char*)_aligned_malloc(65536*sizeof(char),32);\r
970                 if (!obuf)\r
971                 {\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
974                         return 7;\r
975                 }\r
976                 if (setvbuf(o,obuf,_IOFBF,65536))\r
977                 {\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
980                         return 8;\r
981                 }\r
982         }\r
983 \r
984         /* Open the timecode file. */\r
985         FILE *tcf = fopen(argv[3],"w");\r
986         if (!tcf)\r
987         {\r
988                 printf("Error opening timecode file.\n");\r
989                 closeFiles(f, o, NULL, repCount, offsets, true, argv[2], NULL, fbuf, obuf);\r
990                 return 9;\r
991         }\r
992 \r
993         /* Find fps and number of null frames. */\r
994         if (!getInfo(f, frameCount, newFrameCount, fps, repCount, offsets, nmovi_size, nhdrl_size))\r
995         {\r
996                 closeFiles(f, o, tcf, repCount, offsets, true, argv[2], argv[3], fbuf, obuf);\r
997                 return 10;\r
998         }\r
999 \r
1000         /* Create the timecode file. */\r
1001         if (!createTCFile(tcf, newFrameCount, fps, repCount, mode))\r
1002         {\r
1003                 closeFiles(f, o, tcf, repCount, offsets, true, argv[2], argv[3], fbuf, obuf);\r
1004                 return 11;\r
1005         }\r
1006 \r
1007         /* Create the output avi file with NULL frames removed. */\r
1008         if (mode < 3)\r
1009         {\r
1010                 if (!createAVIFile(f, o, newFrameCount, frameCount, offsets, nmovi_size, nhdrl_size))\r
1011                 {\r
1012                         closeFiles(f, o, tcf, repCount, offsets, true, argv[2], argv[3], fbuf, obuf);\r
1013                         return 12;\r
1014                 }\r
1015         }\r
1016 \r
1017         /* Create avs script */\r
1018         if (mode > 4)\r
1019         {\r
1020                 FILE *avsf = fopen(argv[2],"w");\r
1021                 if (!avsf)\r
1022                 {\r
1023                         printf("Error creating avs file.\n");\r
1024                         closeFiles(f, o, tcf, repCount, offsets, true, argv[2], argv[3], fbuf, obuf);\r
1025                         return 13;\r
1026                 }\r
1027                 if (!createAVSFile(avsf, argv[1], newFrameCount, repCount))\r
1028                 {\r
1029                         fclose(avsf);\r
1030                         unlink(argv[2]);\r
1031                         closeFiles(f, o, tcf, repCount, offsets, true, argv[2], argv[3], fbuf, obuf);\r
1032                         return 14;\r
1033                 }\r
1034                 fclose(avsf);\r
1035         }\r
1036 \r
1037         /* Cleanup and finish. */\r
1038         finish = clock();\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
1042         return 0;\r
1043 }