1 /*
2 * avimerge.c
3 *
4 * Copyright (C) Thomas Oestreich - June 2001
5 *
6 * This file is part of transcode, a video stream processing tool
7 *
8 * transcode is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2, or (at your option)
11 * any later version.
12 *
13 * transcode is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with GNU Make; see the file COPYING. If not, write to
20 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
21 *
22 */
23 // TODO: Simplify this code. Immediatly
24
25 #include "transcode.h"
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <string.h>
31
32 #include "aud_scan.h"
33 #include "aud_scan_avi.h"
34
35 #define EXE "avimerge"
36
37 /* AVI_info is no longer in avilib */
38 void AVI_info(avi_t *avifile);
39
version(void)40 void version(void)
41 {
42 printf("%s (%s v%s) (C) 2001-2004 Thomas Oestreich, T. Bitterberg"
43 " 2004-2010 Transcode Team\n",
44 EXE, PACKAGE, VERSION);
45 }
46
usage(int status)47 static void usage(int status)
48 {
49 version();
50 printf("\nUsage: %s [options]\n", EXE);
51 printf(" -o file output file name\n");
52 printf(" -i file1 [file2 [...]] input file(s)\n");
53 printf(" -p file multiplex additional audio track from file\n");
54 printf(" -a num select audio track number from input file [0]\n");
55 printf(" -A num select audio track number in output file [next]\n");
56 printf(" -b n handle vbr audio [autodetect]\n");
57 printf(" -c drop video frames in case audio is missing [off]\n");
58 printf(" -f FILE read AVI comments from FILE [off]\n");
59 printf(" -x FILE read AVI index from FILE [off] (see aviindex(1))\n");
60 exit(status);
61 }
62
63 static char data[SIZE_RGB_FRAME];
64 static char *comfile = NULL;
65 static char *indexfile = NULL;
66 long sum_frames = 0;
67 int is_vbr=1;
68 int drop_video=0;
69
70
merger(avi_t * out,char * file)71 static int merger(avi_t *out, char *file)
72 {
73 avi_t *in;
74 long frames, n, bytes;
75 int key, j, aud_tracks;
76 static int init = 0;
77 static int vid_chunks = 0;
78
79 double fps;
80 static double vid_ms;
81 static double aud_ms[AVI_MAX_TRACKS];
82 int have_printed=0;
83 int do_drop_video=0;
84
85 if (!init) {
86 for (j=0; j<AVI_MAX_TRACKS; j++)
87 aud_ms[j] = 0.0;
88 vid_ms = 0;
89 vid_chunks = 0;
90 init = 1;
91 }
92
93 if(indexfile) {
94 if (NULL == (in = AVI_open_input_indexfile(file, 0, indexfile))) {
95 AVI_print_error("AVI open with indexfile");
96 return(-1);
97 }
98 }
99 else if(NULL == (in = AVI_open_input_file(file,1))) {
100 AVI_print_error("AVI open");
101 return(-1);
102 }
103
104 AVI_seek_start(in);
105 fps = AVI_frame_rate(in);
106 frames = AVI_video_frames(in);
107 aud_tracks = AVI_audio_tracks(in);
108
109 for (n=0; n<frames; ++n) {
110
111 ++vid_chunks;
112 vid_ms = vid_chunks*1000.0/fps;
113
114 // audio
115 for(j=0; j<aud_tracks; ++j) {
116
117 int ret;
118 double old_ms = aud_ms[j];
119
120 AVI_set_audio_track(in, j);
121 AVI_set_audio_track(out, j);
122
123 ret = sync_audio_video_avi2avi (vid_ms, &aud_ms[j], in, out);
124 if (ret<0) {
125 if (ret==-2) {
126 if (aud_ms[j] == old_ms) {
127 do_drop_video = 1;
128 if (!have_printed) {
129 fprintf(stderr, "\nNo audiodata left for track %d->%d (%.2f=%.2f) %s ..\n",
130 AVI_get_audio_track(in), AVI_get_audio_track(out),
131 old_ms, aud_ms[j], (do_drop_video && drop_video)?"breaking (-c)":"continuing");
132 have_printed++;
133 }
134 }
135 } else {
136 fprintf(stderr, "\nAn error happend at frame %ld track %d\n", n, j);
137 }
138 }
139
140 }
141
142 if (do_drop_video && drop_video) {
143 fprintf(stderr, "\n[avimerge] Dropping %ld frames\n", frames-n-1);
144 goto out;
145 }
146
147 // video
148 bytes = AVI_read_frame(in, data, &key);
149
150 if(bytes < 0) {
151 AVI_print_error("AVI read video frame");
152 return(-1);
153 }
154
155 if(AVI_write_frame(out, data, bytes, key)<0) {
156 AVI_print_error("AVI write video frame");
157 return(-1);
158 }
159
160 // progress
161 fprintf(stderr, "[%s] (%06ld-%06ld) (%.2f <-> %.2f)\r", file, sum_frames, sum_frames + n, vid_ms, aud_ms[0]);
162 }
163 out:
164 fprintf(stderr, "\n");
165
166 AVI_close(in);
167
168 sum_frames += n;
169
170 return(0);
171 }
172
173
main(int argc,char * argv[])174 int main(int argc, char *argv[])
175 {
176 avi_t *avifile, *avifile1, *avifile2;
177
178 char *outfile=NULL, *infile=NULL, *audfile=NULL;
179
180 long rate, mp3rate;
181
182 int j, ch, cc=0, track_num=0, out_track_num=-1;
183 int width, height, format=0, format_add, chan, bits, aud_error=0;
184
185 double fps;
186
187 char *codec;
188
189 long offset, frames, n, bytes, aud_offset=0;
190
191 int key;
192
193 int aud_tracks;
194
195 // for mp3 audio
196 FILE *f=NULL;
197 int len, headlen, chan_i, rate_i, mp3rate_i;
198 unsigned long vid_chunks=0;
199 char head[8];
200 off_t pos;
201 double aud_ms = 0.0, vid_ms = 0.0;
202 double aud_ms_w[AVI_MAX_TRACKS];
203
204 ac_init(AC_ALL);
205
206 if(argc==1) usage(EXIT_FAILURE);
207
208 while ((ch = getopt(argc, argv, "A:a:b:ci:o:p:f:x:?hv")) != -1) {
209
210 switch (ch) {
211
212 case 'i':
213
214 if(optarg[0]=='-') usage(EXIT_FAILURE);
215 infile = optarg;
216
217 break;
218
219 case 'A':
220
221 if(optarg[0]=='-') usage(EXIT_FAILURE);
222 out_track_num = atoi(optarg);
223
224 if(out_track_num<-1) usage(EXIT_FAILURE);
225
226 break;
227
228 case 'a':
229
230 if(optarg[0]=='-') usage(EXIT_FAILURE);
231 track_num = atoi(optarg);
232
233 if(track_num<0) usage(EXIT_FAILURE);
234
235 break;
236
237 case 'b':
238
239 if(optarg[0]=='-') usage(EXIT_FAILURE);
240 is_vbr = atoi(optarg);
241
242 if(is_vbr<0) usage(EXIT_FAILURE);
243
244 break;
245
246 case 'c':
247
248 drop_video = 1;
249
250 break;
251
252 case 'o':
253
254 if(optarg[0]=='-') usage(EXIT_FAILURE);
255 outfile = optarg;
256
257 break;
258
259 case 'p':
260
261 if(optarg[0]=='-') usage(EXIT_FAILURE);
262 audfile = optarg;
263
264 break;
265
266 case 'f':
267
268 if(optarg[0]=='-') usage(EXIT_FAILURE);
269 comfile = optarg;
270
271 break;
272
273
274 case 'x':
275
276 if(optarg[0]=='-') usage(EXIT_FAILURE);
277 indexfile = optarg;
278
279 break;
280
281 case 'v':
282 version();
283 exit(EXIT_SUCCESS);
284 case 'h':
285 usage(EXIT_SUCCESS);
286 default:
287 usage(EXIT_FAILURE);
288 }
289 }
290
291 if(outfile == NULL || infile == NULL) usage(EXIT_FAILURE);
292
293 printf("scanning file %s for video/audio parameter\n", infile);
294
295 // open first file for video/audio info read only
296 if(indexfile) {
297 if (NULL == (avifile1 = AVI_open_input_indexfile(infile,0,indexfile))) {
298 AVI_print_error("AVI open with index file");
299 }
300 }
301 else if(NULL == (avifile1 = AVI_open_input_file(infile,1))) {
302 AVI_print_error("AVI open");
303 exit(1);
304 }
305
306 AVI_info(avifile1);
307
308 // safety checks
309
310 if(strcmp(infile, outfile)==0) {
311 printf("error: output filename conflicts with input filename\n");
312 exit(1);
313 }
314
315 ch = optind;
316
317 while (ch < argc) {
318
319 if(tc_file_check(argv[ch]) != 0) {
320 printf("error: file not found\n");
321 exit(1);
322 }
323
324 if(strcmp(argv[ch++], outfile)==0) {
325 printf("error: output filename conflicts with input filename\n");
326 exit(1);
327 }
328 }
329
330 // open output file
331 if(NULL == (avifile = AVI_open_output_file(outfile))) {
332 AVI_print_error("AVI open");
333 exit(1);
334 }
335
336
337 // read video info;
338
339 width = AVI_video_width(avifile1);
340 height = AVI_video_height(avifile1);
341
342 fps = AVI_frame_rate(avifile1);
343 codec = AVI_video_compressor(avifile1);
344
345 //set video in outputfile
346 AVI_set_video(avifile, width, height, fps, codec);
347
348 if (comfile!=NULL)
349 AVI_set_comment_fd(avifile, open(comfile, O_RDONLY));
350
351 //multi audio tracks?
352 aud_tracks = AVI_audio_tracks(avifile1);
353 if (out_track_num < 0) out_track_num = aud_tracks;
354
355 for(j=0; j<aud_tracks; ++j) {
356
357 if (out_track_num == j) continue;
358 AVI_set_audio_track(avifile1, j);
359
360 rate = AVI_audio_rate(avifile1);
361 chan = AVI_audio_channels(avifile1);
362 bits = AVI_audio_bits(avifile1);
363
364 format = AVI_audio_format(avifile1);
365 mp3rate= AVI_audio_mp3rate(avifile1);
366 //printf("TRACK %d MP3RATE %ld VBR %ld\n", j, mp3rate, AVI_get_audio_vbr(avifile1));
367
368 //set next track of output file
369 AVI_set_audio_track(avifile, j);
370 AVI_set_audio(avifile, chan, rate, bits, format, mp3rate);
371 AVI_set_audio_vbr(avifile, AVI_get_audio_vbr(avifile1));
372 }
373
374 if(audfile!=NULL) goto audio_merge;
375
376 // close reopen in merger function
377 AVI_close(avifile1);
378
379 //-------------------------------------------------------------
380
381 printf("merging multiple AVI-files (concatenating) ...\n");
382
383 // extract and write to new files
384
385 printf ("file %02d %s\n", ++cc, infile);
386 merger(avifile, infile);
387
388 while (optind < argc) {
389
390 printf ("file %02d %s\n", ++cc, argv[optind]);
391 merger(avifile, argv[optind++]);
392 }
393
394 // close new AVI file
395
396 AVI_close(avifile);
397
398 printf("... done merging %d file(s) in %s\n", cc, outfile);
399
400 // reopen file for video/audio info
401 if(NULL == (avifile = AVI_open_input_file(outfile,1))) {
402 AVI_print_error("AVI open");
403 exit(1);
404 }
405 AVI_info(avifile);
406
407 return(0);
408
409 //-------------------------------------------------------------
410
411
412 // *************************************************
413 // Merge the audio track of an additional AVI file
414 // *************************************************
415
416 audio_merge:
417
418 printf("merging audio %s track %d (multiplexing) into %d ...\n", audfile, track_num, out_track_num);
419
420 // open audio file read only
421 if(NULL == (avifile2 = AVI_open_input_file(audfile,1))) {
422 int f=open(audfile, O_RDONLY), ret=0;
423 char head[1024], *c;
424 c = head;
425 if (f>0 && (1024 == read(f, head, 1024)) ) {
426 while ((c-head<1024-8) && (ret = tc_probe_audio_header(c, 8))<=0 ) {
427 c++;
428 }
429 close(f);
430
431 if (ret > 0) {
432 aud_offset = c-head;
433 //printf("found atrack 0x%x off=%ld\n", ret, aud_offset);
434 goto merge_mp3;
435 }
436 }
437
438 AVI_print_error("AVI open");
439 exit(1);
440 }
441
442 AVI_info(avifile2);
443
444 //switch to requested track
445
446 if(AVI_set_audio_track(avifile2, track_num)<0) {
447 fprintf(stderr, "invalid audio track\n");
448 }
449
450 rate = AVI_audio_rate(avifile2);
451 chan = AVI_audio_channels(avifile2);
452 bits = AVI_audio_bits(avifile2);
453
454 format = AVI_audio_format(avifile2);
455 mp3rate= AVI_audio_mp3rate(avifile2);
456
457 //set next track
458 AVI_set_audio_track(avifile, out_track_num);
459 AVI_set_audio(avifile, chan, rate, bits, format, mp3rate);
460 AVI_set_audio_vbr(avifile, AVI_get_audio_vbr(avifile2));
461
462 AVI_seek_start(avifile1);
463 frames = AVI_video_frames(avifile1);
464 offset = 0;
465
466 printf ("file %02d %s\n", ++cc, infile);
467
468 for (n=0; n<AVI_MAX_TRACKS; n++)
469 aud_ms_w[n] = 0.0;
470 vid_chunks=0;
471
472 for (n=0; n<frames; ++n) {
473
474 // video
475 bytes = AVI_read_frame(avifile1, data, &key);
476
477 if(bytes < 0) {
478 AVI_print_error("AVI read video frame");
479 return(-1);
480 }
481
482 if(AVI_write_frame(avifile, data, bytes, key)<0) {
483 AVI_print_error("AVI write video frame");
484 return(-1);
485 }
486 ++vid_chunks;
487 vid_ms = vid_chunks*1000.0/fps;
488
489 for(j=0; j<aud_tracks; ++j) {
490
491 if (j == out_track_num) continue;
492 AVI_set_audio_track(avifile1, j);
493 AVI_set_audio_track(avifile, j);
494 chan = AVI_audio_channels(avifile1);
495
496 // audio
497 chan = AVI_audio_channels(avifile1);
498 if(chan) {
499 sync_audio_video_avi2avi(vid_ms, &aud_ms_w[j], avifile1, avifile);
500 }
501 }
502
503
504 // merge additional track
505
506 // audio
507 chan = AVI_audio_channels(avifile2);
508 AVI_set_audio_track(avifile, out_track_num);
509
510 if(chan) {
511 sync_audio_video_avi2avi(vid_ms, &aud_ms, avifile2, avifile);
512 }
513
514 // progress
515 fprintf(stderr, "[%s] (%06ld-%06ld)\r", outfile, offset, offset + n);
516
517 }
518
519 fprintf(stderr,"\n");
520
521 offset = frames;
522
523 //more files to merge?
524
525 AVI_close(avifile1);
526
527 while (optind < argc) {
528
529 printf ("file %02d %s\n", ++cc, argv[optind]);
530
531 if(NULL == ( avifile1 = AVI_open_input_file(argv[optind++],1))) {
532 AVI_print_error("AVI open");
533 goto finish;
534 }
535
536 AVI_seek_start(avifile1);
537 frames = AVI_video_frames(avifile1);
538
539 for (n=0; n<frames; ++n) {
540
541 // video
542 bytes = AVI_read_frame(avifile1, data, &key);
543
544 if(bytes < 0) {
545 AVI_print_error("AVI read video frame");
546 return(-1);
547 }
548
549 if(AVI_write_frame(avifile, data, bytes, key)<0) {
550 AVI_print_error("AVI write video frame");
551 return(-1);
552 }
553
554 ++vid_chunks;
555 vid_ms = vid_chunks*1000.0/fps;
556
557 // audio
558 for(j=0; j<aud_tracks; ++j) {
559
560 if (j == out_track_num) continue;
561 AVI_set_audio_track(avifile1, j);
562 AVI_set_audio_track(avifile, j);
563
564 chan = AVI_audio_channels(avifile1);
565
566 if(chan) {
567 sync_audio_video_avi2avi(vid_ms, &aud_ms_w[j], avifile1, avifile);
568 }
569 }
570
571 // merge additional track
572
573 chan = AVI_audio_channels(avifile2);
574 AVI_set_audio_track(avifile, out_track_num);
575
576 if(chan) {
577 sync_audio_video_avi2avi(vid_ms, &aud_ms, avifile2, avifile);
578 } // chan
579
580 // progress
581 fprintf(stderr, "[%s] (%06ld-%06ld)\r", outfile, offset, offset + n);
582 }
583
584 fprintf(stderr, "\n");
585
586 offset += frames;
587 AVI_close(avifile1);
588 }
589
590 finish:
591
592 // close new AVI file
593
594 printf("... done multiplexing in %s\n", outfile);
595
596 AVI_info(avifile);
597 AVI_close(avifile);
598
599 return(0);
600
601
602 // *************************************************
603 // Merge a raw audio file which is either MP3 or AC3
604 // *************************************************
605
606 merge_mp3:
607
608 f = fopen(audfile,"rb");
609 if (!f) { perror ("fopen"); exit(1); }
610
611 fseek(f, aud_offset, SEEK_SET);
612 len = fread(head, 1, 8, f);
613 format_add = tc_probe_audio_header(head, len);
614 headlen = tc_get_audio_header(head, len, format_add, &chan_i, &rate_i, &mp3rate_i);
615 fprintf(stderr, "... this looks like a %s track ...\n", (format_add==0x55)?"MP3":"AC3");
616
617 fseek(f, aud_offset, SEEK_SET);
618
619 //set next track
620 AVI_set_audio_track(avifile, out_track_num);
621 AVI_set_audio(avifile, chan_i, rate_i, 16, format_add, mp3rate_i);
622 AVI_set_audio_vbr(avifile, is_vbr);
623
624 AVI_seek_start(avifile1);
625 frames = AVI_video_frames(avifile1);
626 offset = 0;
627
628 for (n=0; n<AVI_MAX_TRACKS; ++n)
629 aud_ms_w[n] = 0.0;
630
631 for (n=0; n<frames; ++n) {
632
633 // video
634 bytes = AVI_read_frame(avifile1, data, &key);
635
636 if(bytes < 0) {
637 AVI_print_error("AVI read video frame");
638 return(-1);
639 }
640
641 if(AVI_write_frame(avifile, data, bytes, key)<0) {
642 AVI_print_error("AVI write video frame");
643 return(-1);
644 }
645
646 vid_chunks++;
647 vid_ms = vid_chunks*1000.0/fps;
648
649 for(j=0; j<aud_tracks; ++j) {
650
651 if (j == out_track_num) continue;
652 AVI_set_audio_track(avifile1, j);
653 AVI_set_audio_track(avifile, j);
654 chan = AVI_audio_channels(avifile1);
655
656 if(chan) {
657 sync_audio_video_avi2avi(vid_ms, &aud_ms_w[j], avifile1, avifile);
658 }
659 }
660
661
662 // merge additional track
663
664 if(headlen>4 && !aud_error) {
665 while (aud_ms < vid_ms) {
666 //printf("reading Audio Chunk ch(%ld) vms(%lf) ams(%lf)\n", vid_chunks, vid_ms, aud_ms);
667 pos = ftell(f);
668
669 len = fread (head, 1, 8, f);
670 if (len<=0) { //eof
671 fprintf(stderr, "EOF in %s; continuing ..\n", audfile);
672 aud_error=1;
673 break;
674 }
675
676 if ( (headlen = tc_get_audio_header(head, len, format_add, NULL, NULL, &mp3rate_i))<0) {
677 fprintf(stderr, "Broken %s track #(%d)? skipping\n", (format_add==0x55?"MP3":"AC3"), aud_tracks);
678 aud_ms = vid_ms;
679 aud_error=1;
680 } else { // look in import/tcscan.c for explanation
681 aud_ms += (headlen*8.0)/(mp3rate_i);
682 }
683
684 fseek (f, pos, SEEK_SET);
685
686 len = fread (data, headlen, 1, f);
687 if (len<=0) { //eof
688 fprintf(stderr, "EOF in %s; continuing ..\n", audfile);
689 aud_error=1;
690 break;
691 }
692
693 AVI_set_audio_track(avifile, out_track_num);
694
695 if(AVI_write_audio(avifile, data, headlen)<0) {
696 AVI_print_error("AVI write audio frame");
697 return(-1);
698 }
699
700 }
701 }
702
703 // progress
704 fprintf(stderr, "[%s] (%06ld-%06ld)\r", outfile, offset, offset + n);
705
706 }
707
708 fprintf(stderr,"\n");
709 offset = frames;
710
711 // more files?
712 while (optind < argc) {
713
714 printf ("file %02d %s\n", ++cc, argv[optind]);
715
716 if(NULL == ( avifile1 = AVI_open_input_file(argv[optind++],1))) {
717 AVI_print_error("AVI open");
718 goto finish;
719 }
720
721 AVI_seek_start(avifile1);
722 frames = AVI_video_frames(avifile1);
723
724 for (n=0; n<frames; ++n) {
725
726 // video
727 bytes = AVI_read_frame(avifile1, data, &key);
728
729 if(bytes < 0) {
730 AVI_print_error("AVI read video frame");
731 return(-1);
732 }
733
734 if(AVI_write_frame(avifile, data, bytes, key)<0) {
735 AVI_print_error("AVI write video frame");
736 return(-1);
737 }
738
739 vid_chunks++;
740 vid_ms = vid_chunks*1000.0/fps;
741
742 for(j=0; j<aud_tracks; ++j) {
743
744 if (j == out_track_num) continue;
745 AVI_set_audio_track(avifile1, j);
746 AVI_set_audio_track(avifile, j);
747 chan = AVI_audio_channels(avifile1);
748
749 if(chan) {
750 sync_audio_video_avi2avi(vid_ms, &aud_ms_w[j], avifile1, avifile);
751 }
752 }
753
754 // merge additional track
755 // audio
756
757 if(headlen>4 && !aud_error) {
758 while (aud_ms < vid_ms) {
759 //printf("reading Audio Chunk ch(%ld) vms(%lf) ams(%lf)\n", vid_chunks, vid_ms, aud_ms);
760 pos = ftell(f);
761
762 len = fread (head, 8, 1, f);
763 if (len<=0) { //eof
764 fprintf(stderr, "EOF in %s; continuing ..\n", audfile);
765 aud_error=1; break;
766 }
767
768 if ( (headlen = tc_get_audio_header(head, len, format_add, NULL, NULL, &mp3rate_i))<0) {
769 fprintf(stderr, "Broken %s track #(%d)?\n", (format_add==0x55?"MP3":"AC3"), aud_tracks);
770 aud_ms = vid_ms;
771 aud_error=1;
772 } else { // look in import/tcscan.c for explanation
773 aud_ms += (headlen*8.0)/(mp3rate_i);
774 }
775
776 fseek (f, pos, SEEK_SET);
777
778 len = fread (data, headlen, 1, f);
779 if (len<=0) { //eof
780 fprintf(stderr, "EOF in %s; continuing ..\n", audfile);
781 aud_error=1; break;
782 }
783
784 AVI_set_audio_track(avifile, out_track_num);
785
786 if(AVI_write_audio(avifile, data, headlen)<0) {
787 AVI_print_error("AVI write audio frame");
788 return(-1);
789 }
790
791 }
792 }
793
794 // progress
795 fprintf(stderr, "[%s] (%06ld-%06ld)\r", outfile, offset, offset + n);
796 }
797
798 fprintf(stderr, "\n");
799
800 offset += frames;
801 AVI_close(avifile1);
802 }
803
804
805 if (f) fclose(f);
806
807 printf("... done multiplexing in %s\n", outfile);
808
809 AVI_close(avifile);
810
811 return(0);
812 }
813
814 /*************************************************************************/
815
816 /*
817 * Local variables:
818 * c-file-style: "stroustrup"
819 * c-file-offsets: ((case-label . *) (statement-case-intro . *))
820 * indent-tabs-mode: nil
821 * End:
822 *
823 * vim: expandtab shiftwidth=4:
824 */
825