/* * avimerge.c * * Copyright (C) Thomas Oestreich - June 2001 * * This file is part of transcode, a video stream processing tool * * transcode is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * transcode is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * */ // TODO: Simplify this code. Immediatly #include "transcode.h" #include #include #include #include #include "aud_scan.h" #include "aud_scan_avi.h" #define EXE "avimerge" /* AVI_info is no longer in avilib */ void AVI_info(avi_t *avifile); void version(void) { printf("%s (%s v%s) (C) 2001-2004 Thomas Oestreich, T. Bitterberg" " 2004-2010 Transcode Team\n", EXE, PACKAGE, VERSION); } static void usage(int status) { version(); printf("\nUsage: %s [options]\n", EXE); printf(" -o file output file name\n"); printf(" -i file1 [file2 [...]] input file(s)\n"); printf(" -p file multiplex additional audio track from file\n"); printf(" -a num select audio track number from input file [0]\n"); printf(" -A num select audio track number in output file [next]\n"); printf(" -b n handle vbr audio [autodetect]\n"); printf(" -c drop video frames in case audio is missing [off]\n"); printf(" -f FILE read AVI comments from FILE [off]\n"); printf(" -x FILE read AVI index from FILE [off] (see aviindex(1))\n"); exit(status); } static char data[SIZE_RGB_FRAME]; static char *comfile = NULL; static char *indexfile = NULL; long sum_frames = 0; int is_vbr=1; int drop_video=0; static int merger(avi_t *out, char *file) { avi_t *in; long frames, n, bytes; int key, j, aud_tracks; static int init = 0; static int vid_chunks = 0; double fps; static double vid_ms; static double aud_ms[AVI_MAX_TRACKS]; int have_printed=0; int do_drop_video=0; if (!init) { for (j=0; j%d (%.2f=%.2f) %s ..\n", AVI_get_audio_track(in), AVI_get_audio_track(out), old_ms, aud_ms[j], (do_drop_video && drop_video)?"breaking (-c)":"continuing"); have_printed++; } } } else { fprintf(stderr, "\nAn error happend at frame %ld track %d\n", n, j); } } } if (do_drop_video && drop_video) { fprintf(stderr, "\n[avimerge] Dropping %ld frames\n", frames-n-1); goto out; } // video bytes = AVI_read_frame(in, data, &key); if(bytes < 0) { AVI_print_error("AVI read video frame"); return(-1); } if(AVI_write_frame(out, data, bytes, key)<0) { AVI_print_error("AVI write video frame"); return(-1); } // progress fprintf(stderr, "[%s] (%06ld-%06ld) (%.2f <-> %.2f)\r", file, sum_frames, sum_frames + n, vid_ms, aud_ms[0]); } out: fprintf(stderr, "\n"); AVI_close(in); sum_frames += n; return(0); } int main(int argc, char *argv[]) { avi_t *avifile, *avifile1, *avifile2; char *outfile=NULL, *infile=NULL, *audfile=NULL; long rate, mp3rate; int j, ch, cc=0, track_num=0, out_track_num=-1; int width, height, format=0, format_add, chan, bits, aud_error=0; double fps; char *codec; long offset, frames, n, bytes, aud_offset=0; int key; int aud_tracks; // for mp3 audio FILE *f=NULL; int len, headlen, chan_i, rate_i, mp3rate_i; unsigned long vid_chunks=0; char head[8]; off_t pos; double aud_ms = 0.0, vid_ms = 0.0; double aud_ms_w[AVI_MAX_TRACKS]; ac_init(AC_ALL); if(argc==1) usage(EXIT_FAILURE); while ((ch = getopt(argc, argv, "A:a:b:ci:o:p:f:x:?hv")) != -1) { switch (ch) { case 'i': if(optarg[0]=='-') usage(EXIT_FAILURE); infile = optarg; break; case 'A': if(optarg[0]=='-') usage(EXIT_FAILURE); out_track_num = atoi(optarg); if(out_track_num<-1) usage(EXIT_FAILURE); break; case 'a': if(optarg[0]=='-') usage(EXIT_FAILURE); track_num = atoi(optarg); if(track_num<0) usage(EXIT_FAILURE); break; case 'b': if(optarg[0]=='-') usage(EXIT_FAILURE); is_vbr = atoi(optarg); if(is_vbr<0) usage(EXIT_FAILURE); break; case 'c': drop_video = 1; break; case 'o': if(optarg[0]=='-') usage(EXIT_FAILURE); outfile = optarg; break; case 'p': if(optarg[0]=='-') usage(EXIT_FAILURE); audfile = optarg; break; case 'f': if(optarg[0]=='-') usage(EXIT_FAILURE); comfile = optarg; break; case 'x': if(optarg[0]=='-') usage(EXIT_FAILURE); indexfile = optarg; break; case 'v': version(); exit(EXIT_SUCCESS); case 'h': usage(EXIT_SUCCESS); default: usage(EXIT_FAILURE); } } if(outfile == NULL || infile == NULL) usage(EXIT_FAILURE); printf("scanning file %s for video/audio parameter\n", infile); // open first file for video/audio info read only if(indexfile) { if (NULL == (avifile1 = AVI_open_input_indexfile(infile,0,indexfile))) { AVI_print_error("AVI open with index file"); } } else if(NULL == (avifile1 = AVI_open_input_file(infile,1))) { AVI_print_error("AVI open"); exit(1); } AVI_info(avifile1); // safety checks if(strcmp(infile, outfile)==0) { printf("error: output filename conflicts with input filename\n"); exit(1); } ch = optind; while (ch < argc) { if(tc_file_check(argv[ch]) != 0) { printf("error: file not found\n"); exit(1); } if(strcmp(argv[ch++], outfile)==0) { printf("error: output filename conflicts with input filename\n"); exit(1); } } // open output file if(NULL == (avifile = AVI_open_output_file(outfile))) { AVI_print_error("AVI open"); exit(1); } // read video info; width = AVI_video_width(avifile1); height = AVI_video_height(avifile1); fps = AVI_frame_rate(avifile1); codec = AVI_video_compressor(avifile1); //set video in outputfile AVI_set_video(avifile, width, height, fps, codec); if (comfile!=NULL) AVI_set_comment_fd(avifile, open(comfile, O_RDONLY)); //multi audio tracks? aud_tracks = AVI_audio_tracks(avifile1); if (out_track_num < 0) out_track_num = aud_tracks; for(j=0; j0 && (1024 == read(f, head, 1024)) ) { while ((c-head<1024-8) && (ret = tc_probe_audio_header(c, 8))<=0 ) { c++; } close(f); if (ret > 0) { aud_offset = c-head; //printf("found atrack 0x%x off=%ld\n", ret, aud_offset); goto merge_mp3; } } AVI_print_error("AVI open"); exit(1); } AVI_info(avifile2); //switch to requested track if(AVI_set_audio_track(avifile2, track_num)<0) { fprintf(stderr, "invalid audio track\n"); } rate = AVI_audio_rate(avifile2); chan = AVI_audio_channels(avifile2); bits = AVI_audio_bits(avifile2); format = AVI_audio_format(avifile2); mp3rate= AVI_audio_mp3rate(avifile2); //set next track AVI_set_audio_track(avifile, out_track_num); AVI_set_audio(avifile, chan, rate, bits, format, mp3rate); AVI_set_audio_vbr(avifile, AVI_get_audio_vbr(avifile2)); AVI_seek_start(avifile1); frames = AVI_video_frames(avifile1); offset = 0; printf ("file %02d %s\n", ++cc, infile); for (n=0; n4 && !aud_error) { while (aud_ms < vid_ms) { //printf("reading Audio Chunk ch(%ld) vms(%lf) ams(%lf)\n", vid_chunks, vid_ms, aud_ms); pos = ftell(f); len = fread (head, 1, 8, f); if (len<=0) { //eof fprintf(stderr, "EOF in %s; continuing ..\n", audfile); aud_error=1; break; } if ( (headlen = tc_get_audio_header(head, len, format_add, NULL, NULL, &mp3rate_i))<0) { fprintf(stderr, "Broken %s track #(%d)? skipping\n", (format_add==0x55?"MP3":"AC3"), aud_tracks); aud_ms = vid_ms; aud_error=1; } else { // look in import/tcscan.c for explanation aud_ms += (headlen*8.0)/(mp3rate_i); } fseek (f, pos, SEEK_SET); len = fread (data, headlen, 1, f); if (len<=0) { //eof fprintf(stderr, "EOF in %s; continuing ..\n", audfile); aud_error=1; break; } AVI_set_audio_track(avifile, out_track_num); if(AVI_write_audio(avifile, data, headlen)<0) { AVI_print_error("AVI write audio frame"); return(-1); } } } // progress fprintf(stderr, "[%s] (%06ld-%06ld)\r", outfile, offset, offset + n); } fprintf(stderr,"\n"); offset = frames; // more files? while (optind < argc) { printf ("file %02d %s\n", ++cc, argv[optind]); if(NULL == ( avifile1 = AVI_open_input_file(argv[optind++],1))) { AVI_print_error("AVI open"); goto finish; } AVI_seek_start(avifile1); frames = AVI_video_frames(avifile1); for (n=0; n4 && !aud_error) { while (aud_ms < vid_ms) { //printf("reading Audio Chunk ch(%ld) vms(%lf) ams(%lf)\n", vid_chunks, vid_ms, aud_ms); pos = ftell(f); len = fread (head, 8, 1, f); if (len<=0) { //eof fprintf(stderr, "EOF in %s; continuing ..\n", audfile); aud_error=1; break; } if ( (headlen = tc_get_audio_header(head, len, format_add, NULL, NULL, &mp3rate_i))<0) { fprintf(stderr, "Broken %s track #(%d)?\n", (format_add==0x55?"MP3":"AC3"), aud_tracks); aud_ms = vid_ms; aud_error=1; } else { // look in import/tcscan.c for explanation aud_ms += (headlen*8.0)/(mp3rate_i); } fseek (f, pos, SEEK_SET); len = fread (data, headlen, 1, f); if (len<=0) { //eof fprintf(stderr, "EOF in %s; continuing ..\n", audfile); aud_error=1; break; } AVI_set_audio_track(avifile, out_track_num); if(AVI_write_audio(avifile, data, headlen)<0) { AVI_print_error("AVI write audio frame"); return(-1); } } } // progress fprintf(stderr, "[%s] (%06ld-%06ld)\r", outfile, offset, offset + n); } fprintf(stderr, "\n"); offset += frames; AVI_close(avifile1); } if (f) fclose(f); printf("... done multiplexing in %s\n", outfile); AVI_close(avifile); return(0); } /*************************************************************************/ /* * Local variables: * c-file-style: "stroustrup" * c-file-offsets: ((case-label . *) (statement-case-intro . *)) * indent-tabs-mode: nil * End: * * vim: expandtab shiftwidth=4: */