You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

336 lines
9.0 KiB

/*
* 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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#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:
*/