OSDN Git Service

fix pipe input
[cmcheckwave/cmcheckwave.git] / cmcheckwave.c
1 /*********************************************************************
2   original base program  http://oku.edu.mie-u.ac.jp/~okumura/dumpwave.c
3
4   cmcheckwave.c
5
6 Usage: cmcheckwave filename.wav
7  *********************************************************************/
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13 #include <assert.h>
14
15 static void usage(void){
16         exit(1);
17 }
18
19 unsigned char *get_bytes(FILE *f, int n)
20 {
21         static unsigned char s[16];
22
23         assert (n <= sizeof s);
24         if (fread(s, n, 1, f) != 1) {
25                 fprintf(stderr, "Read error\n");
26                 exit(1);
27         }
28         return s;
29 }
30
31 unsigned long get_ulong(FILE *f)
32 {
33         unsigned char *s = get_bytes(f, 4);
34         return s[0] + 256LU * (s[1] + 256LU * (s[2] + 256LU * s[3]));
35 }
36
37 unsigned get_ushort(FILE *f)
38 {
39         unsigned char *s = get_bytes(f, 2);
40         return s[0] + 256U * s[1];
41 }
42
43 typedef struct {
44         int  stsec;
45         int  edsec;
46         int  diffs;
47         char cmflg;
48         char honpen;
49 }muonst;
50
51 static muonst m[1000];
52 static muonst h[100];
53 static int verbose=0;
54 static int noaudioencode=0;
55 static char *wkfilename=NULL;
56 static int defmuon=250;
57 static int defmax=9;
58
59 static char *MP4BOXCMD="/usr/local/bin/MP4Box -quiet -noprog";
60 static char *MP4BOXCMDRAPSTR="Adjusting chunk start time to previous random access at ";
61 static char *SOXCMD="/usr/local/bin/sox";
62 static char *FFMPEGCMD="/usr/local/bin/ffmpeg";
63 static char *AACENCCMD="/usr/local/bin/aacplusenc";
64
65 FILE *checkMP4(FILE *f,char *filename)
66 {
67         char readbuf[20];
68         char cmdbuf[1024];
69         FILE *pp;
70
71         memset(readbuf,0,sizeof(readbuf));
72         fread(readbuf,sizeof(readbuf),1,f);
73         rewind(f);
74         if (!strstr(readbuf+4,"ftypisom")) {
75                 return NULL;
76         }
77         sprintf(cmdbuf,"%s -v 0 -i %s -f wav pipe: 2>/dev/null",FFMPEGCMD,filename);
78         pp = popen(cmdbuf,"r");
79         if (pp == NULL) return NULL;
80         fclose(f);
81         wkfilename = strdup(filename);
82         return pp;
83 }
84
85 FILE *openpipeffmpeg(char *filename)
86 {
87         char cmdbuf[1024];
88         FILE *pp;
89
90
91
92 }
93
94 int checkMP4RAP(int stsec,int edsec)
95 {
96         FILE *pp;
97         char pbuf[1024];
98         char cmdbuf[1024];
99         float rap;
100
101         if (wkfilename==NULL) return stsec;
102
103         sprintf(cmdbuf,"%s -splitx %.2f:%.2f %s -out /dev/null",MP4BOXCMD,edsec/1000.0,(edsec+10000)/1000.0,wkfilename);
104
105         rap=0.0;
106         pp = popen(cmdbuf,"r");
107         if (pp == NULL) return stsec;
108         while(fgets(pbuf,1024,pp)!=NULL){
109                 if (strstr(pbuf,MP4BOXCMDRAPSTR)) {
110                         sscanf(pbuf+strlen(MP4BOXCMDRAPSTR),"%f",&rap);
111                         //TODO rapはCM開始フレームの秒数なのでちょっと戻す。
112                         //     フレームレートとか調べないとだめだな・・・
113                         rap = rap - 0.04;
114                 }
115         }
116         pclose(pp);
117         if (rap > 0 && stsec < rap*1000.0) return rap*1000.0;
118         else return stsec;
119
120 }
121 int dumpinfo(int mcnt)
122 {
123         int honstart,hcnt,totalsec;
124         int i;
125
126         honstart=0;
127         hcnt=0;
128         totalsec=0;
129         printf("#!/bin/sh\n# cmcheckwave %s\n#\n",wkfilename?wkfilename:"");
130         //カットするため、一連のCM,本編時間を結合
131         for(i=0;i<mcnt;i++) {
132                 printf("# %.2f %.2f diff %.2f %s\n",m[i].stsec/1000.0,m[i].edsec/1000.0,m[i].diffs/1000.0,m[i].cmflg?"CM":"");
133                 //本編開始位置をマーク
134                 if ((m[i].cmflg==0)&&(honstart==0)) {
135                         honstart=1;
136                         if (i==0) h[hcnt].stsec = 0;
137                         else h[hcnt].stsec = m[i-1].edsec+(defmuon*0.5);
138                 }
139                 else {
140                         //終了位置をマーク
141                         if ((m[i].cmflg==1)&&(honstart==1)) {
142                                 honstart=0;
143                                 //h[hcnt].edsec = m[i-1].stsec;
144                                 h[hcnt].edsec = checkMP4RAP(m[i-1].stsec,m[i-1].edsec);
145                                 totalsec += h[hcnt].edsec - h[hcnt].stsec;
146                                 hcnt++;
147                         }
148                 }
149         }
150         if (honstart==1) {
151                 h[hcnt].edsec = checkMP4RAP(m[i-1].stsec,m[i-1].edsec);
152                 totalsec += h[hcnt].edsec - h[hcnt].stsec;
153                 hcnt++;
154         }
155
156         printf("# total %.2f\n\n",totalsec/1000.0);
157
158         for(i=0;i<hcnt;i++) {
159                 if (wkfilename) {
160                         printf("%s -splitx %.2f:%.2f %s -out %s.%d%s\n",MP4BOXCMD,h[i].stsec/1000.0,h[i].edsec/1000.0,wkfilename,wkfilename,i,noaudioencode?".mp4":"");
161                         if (!noaudioencode) {
162                                 printf("%s -v 0 -i %s.%d -vn %s.%d.wav\n",FFMPEGCMD,wkfilename,i,wkfilename,i);
163                                 printf("%s -v 0 -i %s.%d -an -vcodec copy %s.%d.mp4\n",FFMPEGCMD,wkfilename,i,wkfilename,i);
164                         }
165                 }
166                 else {
167                         printf("# %s -splitx %.2f:%.2f \n",MP4BOXCMD,h[i].stsec/1000.0,h[i].edsec/1000.0);
168                 }
169         }
170         if (wkfilename) {
171                 if (!noaudioencode) {
172                         printf("%s ",SOXCMD);
173                         for(i=0;i<hcnt;i++) {
174                                 printf(" %s.%d.wav ",wkfilename,i);
175                         }
176                         printf(" %s.wav\n",wkfilename);
177                         printf("%s %s.wav %s.aac 60\n",AACENCCMD,wkfilename,wkfilename);
178                 }
179                 printf("%s ",MP4BOXCMD);
180                 for(i=0;i<hcnt;i++) {
181                         printf(" -cat %s.%d.mp4 ",wkfilename,i);
182                 }
183                 printf(" %s-new.mp4\n",wkfilename);
184
185                 if (!noaudioencode)
186                         printf("%s -add %s.aac %s-new.mp4\n",MP4BOXCMD,wkfilename,wkfilename);
187
188                 printf("rm -f %s.*\n\n",wkfilename);
189         }
190 }
191
192 int rechecktext(FILE *f)
193 {
194         char rbuf[1024];
195         char fname[1024];
196         char cm[100];
197         char wk1[100];
198         char wk2[100];
199         char wk3[100];
200         char wk4[100];
201         int cnt,pgst,len;
202         float in1,in2,in3;
203
204
205         rewind(f);
206         cnt=0;
207
208         pgst=0;
209         fname[0]=0x00;
210
211         while(fgets(rbuf,1024,f)!=NULL){
212                 if (strstr(rbuf,"# cmcheckwave ")) {
213                         pgst=1;
214                         if (strlen(rbuf+14)-1>0) {
215                                 wkfilename = malloc(strlen(rbuf+14));
216                                 strncpy(wkfilename,rbuf+14,strlen(rbuf+14)-1);
217                         }
218                         fgets(rbuf,1024,f);
219                         continue;
220                 }
221                 if (strstr(rbuf,"# total "))
222                         pgst=0;
223                 if (pgst) {
224                         wk4[0]=0x00;
225                         sscanf(rbuf,"# %s %s diff %s %s",wk1,wk2,wk3,wk4);
226
227                         m[cnt].stsec = (int)(atof(wk1)*1000.0);
228                         m[cnt].edsec = (int)(atof(wk2)*1000.0);
229                         m[cnt].diffs = (int)(atof(wk3)*1000.0);
230                         if (strstr(wk4,"CM")) m[cnt].cmflg=1;
231                         else m[cnt].cmflg=0;
232                         m[cnt].honpen = 0;
233                         cnt++;
234                 }
235
236         }
237         return dumpinfo(cnt);
238
239
240 }
241
242 int cmcheckwave(FILE *f)
243 {
244         int i,j, x, channels, bits;
245         unsigned long len;
246         long count;
247         unsigned char s[5];
248         unsigned long bsec;
249         int readed,loop,max,totalsec,muonstartsec,kankaku;
250         double dul,diffs;
251         char cm[10];
252         int mcnt,hcnt,rcnt,readbufsz;
253         unsigned char *readbuf;
254         int honstart;
255         int cmwork;
256
257
258         if (memcmp(get_bytes(f, 4), "RIFF", 4) != 0) {
259                 //   fprintf(stderr, "Not a 'RIFF' format\n");
260                 return -1;
261         }
262         //fprintf(stderr, "[RIFF] (%lu bytes)\n", get_ulong(f));
263         get_ulong(f);
264         if (memcmp(get_bytes(f, 8), "WAVEfmt ", 8) != 0) {
265                 // fprintf(stderr, "Not a 'WAVEfmt ' format\n");
266                 return -1;
267         }
268         len = get_ulong(f);
269         //fprintf(stderr, "[WAVEfmt ] (%lu bytes)\n", len);
270         //fprintf(stderr, "  Data type = %u (1 = PCM)\n", get_ushort(f));
271         get_ushort(f);
272         channels = get_ushort(f);
273         //fprintf(stderr, "  Number of channels = %u (1 = mono, 2 = stereo)\n", channels);
274         //fprintf(stderr, "  Sampling rate = %luHz\n", get_ulong(f));
275         get_ulong(f);
276         bsec = get_ulong(f);
277         //fprintf(stderr, "  Bytes / second = %lu\n", bsec);
278         //fprintf(stderr, "  Bytes x channels = %u\n", get_ushort(f));
279         get_ushort(f);
280         bits = get_ushort(f);
281         //fprintf(stderr, "  Bits / sample = %u\n", bits);
282         for (i = 16; i < len; i++)
283                 fgetc(f);
284         while (fread(s, 4, 1, f) == 1) {
285                 len = get_ulong(f);
286                 s[4] = 0;
287                 //fprintf(stderr, "[%s] (%lu bytes)\n", s, len);
288                 if (memcmp(s, "data", 4) == 0) break;
289                 for (i = 0; i < len; i++)
290                         fgetc(f);
291         }
292
293         readed=max=totalsec=kankaku=mcnt=0;
294         muonstartsec=-1;
295         loop=1;
296         readbuf=malloc(4096*1000);
297         while(rcnt=fread(readbuf,1,4096*1000,f)) {
298                 for(readbufsz=0;readbufsz<rcnt;) {
299                         for (i = 0; i < channels; i++) {
300                                 if (bits <= 8) {
301                                         //if ((x = fgetc(f)) == EOF) {loop=0;break;}
302                                         x = readbuf[readbufsz];
303                                         readed++;
304                                         readbufsz++;
305                                         x -= 128;
306                                 } else {
307                                         //if (fread(s, 2, 1, f) != 1) {loop=0;break;}
308                                         //x = (short)(s[0] + 256 * s[1]);
309                                         x = (short)(readbuf[readbufsz+0] + 256 * readbuf[readbufsz+1]);
310                                         readed+=2;
311                                         readbufsz+=2;
312                                 }
313                                 if (x > max) max = x;
314                                 //printf("%d", x);
315                                 //if (i != channels - 1) printf("\t");
316                         }
317                         //printf("\n");
318                         if (readed % (bsec/100) == 0) {
319                                 //無音開始
320                                 if (muonstartsec==-1 && max < defmax) {
321                                         muonstartsec = totalsec;
322                                 }
323                                 else {
324                                         // 無音終わり
325                                         if (muonstartsec>=0 && max >= defmax) {
326                                                 // 無音が300(defmuon)msより大きい
327                                                 if (totalsec - muonstartsec > defmuon){
328
329                                                         m[mcnt].stsec = muonstartsec;
330                                                         m[mcnt].edsec = totalsec;
331                                                         m[mcnt].diffs = totalsec - kankaku;
332                                                         m[mcnt].cmflg = 0;
333                                                         m[mcnt].honpen = 0;
334
335                                                         dul = (totalsec-muonstartsec)/1000.0;
336                                                         diffs = (totalsec-kankaku)/1000.0;
337
338                                                         if ((diffs >  14.5) && (diffs < 15.5)) m[mcnt].cmflg=1;
339                                                         if ((diffs >  29.5) && (diffs < 30.5)) m[mcnt].cmflg=1;
340                                                         if ((diffs >  59.5) && (diffs < 60.5)) m[mcnt].cmflg=1;
341
342                                                         kankaku=totalsec;
343                                                         mcnt++;
344                                                 }
345                                                 muonstartsec=-1;
346                                         }
347                                 }
348                                 max=0;
349                                 totalsec += 10;
350                         }
351                 }
352         }
353         if (mcnt > 1) {
354                 //本編前CMチェック
355                 if ((m[0].cmflg==0) && (m[0].diffs < 15000))
356                         m[0].cmflg=1;
357                 //細切れCMのたしこみ
358                 for(i=1;i<mcnt-1;i++) {
359                         // 本編で31秒以下が連続だったら、次の31秒以上の本編もしくはCMまでの時間をチェック
360                         if (m[i].cmflg==0 && m[i].diffs < 31000  && m[i+1].cmflg==0 && m[i+1].diffs < 31000) {
361                                 cmwork=0;
362                                 for(j=i;j<mcnt;j++) {
363                                         if (m[j].cmflg==1) break;
364                                         if (m[j].diffs > 31000) break;
365                                         cmwork = cmwork + m[j].diffs;
366                                 }
367                                 //合計時間を15秒で割ってcm時間っぽいならばCMとする。
368                                 // TODO 30(15)秒以下の条件付けがいる?60秒どうする?
369                                 if (cmwork%15000>14500 || cmwork%15000<500) {
370                                         for(j=i;j<mcnt;j++) {
371                                                 if (m[j].cmflg==1) break;
372                                                 if (m[j].diffs > 31000) break;
373                                                 m[j].cmflg=1;
374                                         }
375                                 }
376                         }
377
378                 }
379                 // 最終CMチェック
380                 if ((m[mcnt-1].cmflg==0) && (m[mcnt-1].diffs < 15000))
381                         m[mcnt-1].cmflg=1;
382                 //短い本編・提供などの処理
383                 for(i=1;i<mcnt-1;i++) {
384                         //本編で46秒以下かつ、前後がCMの場合CM14-16,29-31,44-46秒でもCMとする。
385                         if (m[i].cmflg==0 && m[i].diffs < 46000 && m[i-1].cmflg==1 && m[i+1].cmflg==1) {
386                                 if ((m[i].diffs > 14000) && (m[i].diffs < 16000)) m[i].cmflg=1;
387                                 if ((m[i].diffs > 29000) && (m[i].diffs < 31000)) m[i].cmflg=1;
388                                 if ((m[i].diffs > 44000) && (m[i].diffs < 46000)) m[i].cmflg=1;
389                                 // 0.9秒以下(おそらく前後CMのあまり時間)
390                                 if (m[i].diffs < 900) m[i].cmflg=1;
391
392                                 //10,5秒のときは提供とみなし、その前を本編にする。
393                                 //TODO 15秒提供は判別不能・・・
394                                 if ((m[i].diffs > 9500) && (m[i].diffs < 10500)) m[i-1].cmflg=0;
395                                 if ((m[i].diffs > 4500) && (m[i].diffs < 5500)) m[i-1].cmflg=0;
396                         }
397                         // TODO 前後が本編で単独でCMの場合は本編とする?
398                         // 46秒以下のチェックも?
399                         // if (m[i].cmflg==1 && m[i-1].cmflg==0 && m[i+1].cmflg==0) {
400                         //      m[i].cmflg=0;
401                         // }
402                 }
403
404         }
405         return dumpinfo(mcnt);
406 }
407
408 int main(int argc, char *argv[])
409 {
410         extern char *optarg;
411         extern int optind, opterr;
412         int i,ch;
413         FILE *f,*p;
414         int ret;
415         char *tmpenv;
416         ret = -1;
417
418         while ((ch = getopt(argc, argv, "adb:m:v:")) != -1){
419                 switch (ch){
420                         case 'a':
421                                 noaudioencode=1;
422                                 break;
423                         case 'd':
424                                 verbose=1;
425                                 break;
426                         case 'b':
427                                 wkfilename=optarg;
428                                 break;
429                         case 'm':
430                                 defmuon=atoi(optarg);
431                                 break;
432                         case 'v':
433                                 defmax=atoi(optarg);
434                                 break;
435                         default:
436                                 usage();
437                 }
438         }
439         argc -= optind;
440         argv += optind;
441
442         if (argc != 1) {
443                 usage();
444                 return 0;
445         }
446         if (tmpenv=getenv("FFMPEG")) FFMPEGCMD=tmpenv;
447         if (tmpenv=getenv("SOX")) SOXCMD=tmpenv;
448         if (tmpenv=getenv("MP4BOX")) MP4BOXCMD=tmpenv;
449         if (tmpenv=getenv("MP4BOXCMDRAPSTR")) MP4BOXCMDRAPSTR=tmpenv;
450         if (tmpenv=getenv("AACENC")) AACENCCMD=tmpenv;
451
452         ret=0;
453         p=NULL;
454         if (strcmp(argv[0],"-")==0)
455                 f = stdin;
456         else {
457                 f = fopen(argv[0],"rb");
458                 if (f) {
459                         p = checkMP4(f,argv[0]);
460                 }
461                 else
462                         return -1;
463         }
464
465         if (p)  ret = cmcheckwave(p);
466         else    ret = cmcheckwave(f);
467         // -1 のときはテキストとして再チェック
468         if (ret == -1) rechecktext(f);
469         if (p) pclose(p);
470         else fclose(f);
471
472         return ret;
473 }
474
475
476
477