/* * avifix.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. * */ #include "transcode.h" #include #include #include #include #include #include #include #include "avilib/avilib.h" #define EXE "avifix" /* AVI_info is no longer in avilib */ void AVI_info(avi_t *avifile); void version(void) { printf("%s (%s v%s) (C) 2001-2003 Thomas Oestreich," " 2003-2010 Transcode Team\n", EXE, PACKAGE, VERSION); } static void usage(int status) { version(); printf("\nUsage: %s [options]\n", EXE); printf(" -i name AVI file name\n"); printf(" -F string video codec FOURCC\n"); printf(" -f val1,val2 video frame rate (fps=val1/val2)\n"); printf(" -N 0xnn audio format identifier\n"); printf(" -b bitrate audio encoder bitrate (kbps)\n"); printf(" -e r[,b[,c]] audio stream parameter (samplerate,bits,channels)\n"); printf(" -a num audio track number [0]\n"); printf(" -d print debug information\n"); printf(" -v print version\n"); exit(status); } /* read or die! */ static void hdr_read(const char *tag, int fd, void *hdr, size_t bytes) { ssize_t r = read(fd, hdr, bytes); if (bytes != r) { tc_log_error(EXE, "(%s) error reading AVI-file", tag); exit(1); } } /* write or die! */ static void hdr_write(const char *tag, int fd, const void *hdr, size_t bytes) { ssize_t w = write(fd, hdr, bytes); if (bytes != w) { tc_log_error(EXE, "(%s) error writing AVI-file", tag); exit(1); } } #define VALIDATE_OPTION if (optarg[0]=='-') usage(EXIT_FAILURE) enum { /* video in the lower bits */ CHANGE_VIDEO_FOURCC = (1UL), CHANGE_VIDEO_FPS = (1UL << 1), /* audio in the higher bits */ CHANGE_AUDIO_FMT = (1UL << 16), CHANGE_AUDIO_BR = (1UL << 17), CHANGE_AUDIO_RATE = (1UL << 18), CHANGE_AUDIO_BITS = (1UL << 19), CHANGE_AUDIO_CHANS = (1UL << 20) }; #define CHANGE_NOTHING (0UL) #define NEED_AUDIO_CHANGE(FLAGS) (FLAGS & 0xFFFF0000) #define NEED_VIDEO_CHANGE(FLAGS) (FLAGS & 0x0000FFFF) int main(int argc, char *argv[]) { struct common_struct rtf; struct AVIStreamHeader ash, vsh; avi_t *avifile; int err, fd, id = 0, track_num = 0, n, ch, debug = TC_FALSE; int brate = 0, val1 = 0, val2 = 1, a_rate, a_chan, a_bits; long ah_off = 0, af_off = 0, vh_off = 0, vf_off = 0; char codec[5], *str = NULL, *filename = NULL; uint32_t change = CHANGE_NOTHING; ac_init(AC_ALL); if (argc==1) usage(EXIT_FAILURE); while ((ch = getopt(argc, argv, "df:i:N:F:vb:e:a:?h")) != -1) { switch (ch) { case 'N': VALIDATE_OPTION; id = strtol(optarg, NULL, 16); if (id < 0) { tc_log_error(EXE, "invalid parameter set for option -N"); } else { change |= CHANGE_AUDIO_FMT; } break; case 'a': VALIDATE_OPTION; track_num = atoi(optarg); if (track_num < 0) usage(EXIT_FAILURE); break; case 'f': VALIDATE_OPTION; n = sscanf(optarg,"%d,%d", &val1, &val2); if (n != 2 || val1 < 0 || val2 < 0) { tc_log_error(EXE, "invalid parameter set for option -f"); } else { change |= CHANGE_VIDEO_FPS; } break; case 'F': VALIDATE_OPTION; str = optarg; if(strlen(str) > 4 || strlen(str) == 0) { tc_log_error(EXE, "invalid parameter set for option -F"); } else { change |= CHANGE_VIDEO_FOURCC; } break; case 'i': VALIDATE_OPTION; filename = optarg; break; case 'b': VALIDATE_OPTION; brate = atoi(optarg); change |= CHANGE_AUDIO_BR; break; case 'v': version(); exit(0); case 'e': VALIDATE_OPTION; n = sscanf(optarg,"%d,%d,%d", &a_rate, &a_bits, &a_chan); switch (n) { case 3: change |= CHANGE_AUDIO_RATE; case 2: change |= CHANGE_AUDIO_BITS; case 1: change |= CHANGE_AUDIO_CHANS; break; default: tc_log_error(EXE, "invalid parameter set for option -e"); } break; case 'd': debug = TC_TRUE; break; case 'h': usage(EXIT_SUCCESS); default: usage(EXIT_FAILURE); } } if (!filename) usage(EXIT_FAILURE); tc_log_info(EXE, "scanning AVI-file %s for header information", filename); avifile = AVI_open_input_file(filename, 1); if (!avifile) { AVI_print_error("AVI open"); exit(1); } AVI_info(avifile); if (AVI_set_audio_track(avifile, track_num) < 0) { tc_log_error(EXE, "invalid audio track"); } ah_off = AVI_audio_codech_offset(avifile); af_off = AVI_audio_codecf_offset(avifile); vh_off = AVI_video_codech_offset(avifile); vf_off = AVI_video_codecf_offset(avifile); if (debug) { tc_log_info(EXE, "offsets: ah=%li af=%li vh=%li vf=%li", ah_off, af_off, vh_off, vf_off); } AVI_close(avifile); fd = open(filename, O_RDWR); if (fd < 0) { perror("open"); exit(1); } lseek(fd, vh_off, SEEK_SET); hdr_read("video codec [h]", fd, codec, 4); codec[4] = 0; lseek(fd, vf_off, SEEK_SET); hdr_read("video codec [f]", fd, codec, 4); codec[4] = 0; if (change & CHANGE_VIDEO_FPS) { lseek(fd, vh_off-4, SEEK_SET); hdr_read("video fps", fd, &vsh, sizeof(vsh)); vsh.dwRate = (long)val1; vsh.dwScale = (long)val2; lseek(fd, vh_off-4, SEEK_SET); hdr_write("video fps", fd, &vsh, sizeof(vsh)); } if (change & CHANGE_VIDEO_FOURCC) { lseek(fd,vh_off,SEEK_SET); if (strncmp(str,"RGB",3) == 0) { hdr_write("video 4cc", fd, codec, 4); } else { hdr_write("video 4cc", fd, str, 4); } lseek(fd,vf_off,SEEK_SET); if(strncmp(str,"RGB",3)==0) { memset(codec, 0, 4); hdr_write("video 4cc", fd, codec, 4); } else { hdr_write("video 4cc", fd, str, 4); } } if (NEED_AUDIO_CHANGE(change)) { lseek(fd, ah_off, SEEK_SET); hdr_read("audio header [h]", fd, &ash, sizeof(ash)); lseek(fd, af_off, SEEK_SET); hdr_read("audio header [f]", fd, &rtf, sizeof(rtf)); if (change & CHANGE_AUDIO_FMT) { rtf.wFormatTag = (unsigned short) id; } if (change & CHANGE_AUDIO_BR) { rtf.dwAvgBytesPerSec = (long) 1000*brate/8; ash.dwRate = (long) 1000*brate/8; ash.dwScale = 1; } if (change & CHANGE_AUDIO_CHANS) { rtf.wChannels = (short) a_chan; } if (change & CHANGE_AUDIO_BITS) { rtf.wBitsPerSample = (short) a_bits; } if (change & CHANGE_AUDIO_RATE) { rtf.dwSamplesPerSec = (long) a_rate; } lseek(fd, ah_off ,SEEK_SET); hdr_write("audio header [h]", fd, &ash, sizeof(ash)); lseek(fd, af_off ,SEEK_SET); hdr_write("audio header [f]", fd, &rtf, sizeof(rtf)); } err = close(fd); if (err) { perror("close"); exit(1); } avifile = AVI_open_input_file(filename, 1); if (!avifile) { AVI_print_error("AVI open"); exit(1); } tc_log_info(EXE, "updated AVI file %s", filename); AVI_info(avifile); 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: */