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.
915 lines
23 KiB
915 lines
23 KiB
/*
|
|
* aviindex.c
|
|
*
|
|
* extracts the index of an AVI file for easy seeking with --nav_seek
|
|
*
|
|
* Copyright (C) Tilmann Bitterberg - June 2003
|
|
*
|
|
* 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 <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
|
|
#include "libtc/xio.h"
|
|
|
|
#include "aud_scan.h"
|
|
|
|
#define EXE "aviindex"
|
|
|
|
/* AVI_info is no longer in avilib */
|
|
void AVI_info(avi_t *avifile);
|
|
|
|
void version(void)
|
|
{
|
|
printf("%s (%s v%s) (C) 2003-2004 Tilmann 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\n");
|
|
printf(" -i file input file\n");
|
|
printf(" -f force the use of the existing index\n");
|
|
printf(" only to use when avi > 2GB, because\n");
|
|
printf(" the default is to -n with big files\n");
|
|
printf(" -n read index in \"smart\" mode: don't use the existing index\n");
|
|
printf(" -x don't use the existing index to generate the keyframes\n");
|
|
printf(" this flag forces -n\n");
|
|
printf(" -v print version\n");
|
|
exit(status);
|
|
}
|
|
|
|
#define PAD_EVEN(x) ( ((x)+1) & ~1 )
|
|
static unsigned long str2ulong(unsigned char *str)
|
|
{
|
|
return ( str[0] | (str[1]<<8) | (str[2]<<16) | (str[3]<<24) );
|
|
}
|
|
|
|
#define AVI_MAX_LEN (UINT_MAX-(1<<20)*16-2048)
|
|
|
|
static inline uint32_t SWAP(uint32_t a) {
|
|
#ifdef WORDS_BIGENDIAN
|
|
return ( (a<<24&0xff000000) | (a<< 8&0x00ff0000) |
|
|
(a>> 8&0x0000ff00) | (a>>24&0x000000ff));
|
|
#else
|
|
return a;
|
|
#endif
|
|
}
|
|
|
|
static inline int avi_stream_id(unsigned int id){
|
|
unsigned char *p=(unsigned char *)&id;
|
|
unsigned char a,b;
|
|
#if WORDS_BIGENDIAN
|
|
a=p[3]-'0'; b=p[2]-'0';
|
|
#else
|
|
a=p[0]-'0'; b=p[1]-'0';
|
|
#endif
|
|
if(a>9 || b>9) return 100; // invalid ID
|
|
return a*10+b;
|
|
}
|
|
|
|
// returns 1 for video
|
|
// 2 for 1st audio
|
|
// 3 for 2nd audio
|
|
static int avi_stream_nr(int id)
|
|
{
|
|
unsigned char *p = (unsigned char *)&id;
|
|
|
|
#if WORDS_BIGENDIAN
|
|
if (p[1] == 'd') {
|
|
return 1;
|
|
} else {
|
|
int res = (p[3]-'0')+(p[2]-'0');
|
|
res = (res==0)?2:res+1;
|
|
return res;
|
|
}
|
|
#else
|
|
if (p[2] == 'd') {
|
|
return 1;
|
|
} else {
|
|
int res = (p[0]-'0')+(p[1]-'0');
|
|
res = (res==0)?2:res+1;
|
|
return res;
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
const int LEN=10;
|
|
|
|
typedef enum {
|
|
UNKNOWN = 0,
|
|
RIFF,
|
|
AVIIDX1,
|
|
MPIDX1,
|
|
} ftype_t;
|
|
|
|
typedef struct {
|
|
uint32_t ckid;
|
|
uint32_t dwFlags;
|
|
uint32_t dwChunkOffset;
|
|
uint32_t dwChunkLength;
|
|
} AVIINDEXENTRY;
|
|
|
|
static int aviidx1_to_mpidx1(char *in_file, FILE *out_fd)
|
|
{
|
|
char *data;
|
|
FILE *in;
|
|
int size=0, i;
|
|
AVIINDEXENTRY *idx;
|
|
char line[255], d;
|
|
|
|
in = fopen (in_file, "r");
|
|
if (!in) return 1;
|
|
|
|
// skip header
|
|
fgets (line, 255, in);
|
|
fgets (line, 255, in);
|
|
while (fgets(line, 255, in)) {
|
|
d = line[5] - '1';
|
|
if (d == 0) {
|
|
size++;
|
|
} else if (d == 1 || d == 2 || d == 3 || d == 4 ||
|
|
d == 5 || d == 6 || d == 7 || d == 8 ) {
|
|
size++;
|
|
} else continue;
|
|
}
|
|
data = malloc (size * sizeof (AVIINDEXENTRY));
|
|
fseek(in, 0, SEEK_SET);
|
|
fgets (line, 255, in); fgets (line, 255, in);
|
|
|
|
i = size;
|
|
idx = &((AVIINDEXENTRY *)data)[0];
|
|
while (fgets(line, 255, in) && i--) {
|
|
char *c=line;
|
|
idx->ckid=*(int *)c;
|
|
idx->ckid=SWAP(idx->ckid);
|
|
c = strchr(c, ' ')+1; // type
|
|
c = strchr(c, ' ')+1; // chunk
|
|
c = strchr(c, ' ')+1; // chunk/type
|
|
c = strchr(c, ' ')+1; // pos
|
|
idx->dwChunkOffset = strtol(c, &c, 10);
|
|
idx->dwChunkLength = strtol(c+1, &c, 10);
|
|
idx->dwFlags = strtol(c+1, &c, 10);
|
|
idx->dwFlags = idx->dwFlags?0x10:0;
|
|
idx++;
|
|
|
|
}
|
|
fwrite ("MPIDX1", 6, 1, out_fd);
|
|
fwrite (&size, 4, 1, out_fd);
|
|
fwrite (data, sizeof(AVIINDEXENTRY), size, out_fd);
|
|
|
|
free(data);
|
|
fclose (in);
|
|
fclose(out_fd);
|
|
return 0;
|
|
}
|
|
|
|
static int mpidx1_to_aviidx1(char *in_file, FILE *out_fd)
|
|
{
|
|
char head[10];
|
|
char *data;
|
|
FILE *in;
|
|
int size, i;
|
|
AVIINDEXENTRY *idx;
|
|
int streams[100];
|
|
|
|
in = fopen (in_file, "r");
|
|
if (!in) return 1;
|
|
fread (head, 10, 1, in);
|
|
|
|
// header. Magic tag is AVIIDX1
|
|
fprintf(out_fd, "AVIIDX1 # Generated by %s (%s-%s)\n", EXE, PACKAGE, VERSION); // Magic
|
|
fprintf(out_fd, "TAG TYPE CHUNK CHUNK/TYPE POS LEN KEY MS\n");
|
|
|
|
size = *(int *)(head+6);
|
|
data = malloc (size * sizeof(AVIINDEXENTRY));
|
|
memset (streams, 0, sizeof(streams));
|
|
if (size != fread (data, sizeof(AVIINDEXENTRY), size, in)) {
|
|
perror("fread"); return 1;
|
|
}
|
|
for (i = 0; i<size; i++) {
|
|
uint32_t ckid;
|
|
idx = &((AVIINDEXENTRY *)data)[i];
|
|
ckid = SWAP(idx->ckid);
|
|
fprintf(out_fd,
|
|
"%.4s %d %d %d %d %d %d 0\n",
|
|
(char *)&ckid,
|
|
avi_stream_nr(idx->ckid),
|
|
i,
|
|
streams[avi_stream_id(idx->ckid)],
|
|
idx->dwChunkOffset,
|
|
idx->dwChunkLength,
|
|
idx->dwFlags?1:0);
|
|
|
|
streams[avi_stream_id(idx->ckid)]++;
|
|
}
|
|
|
|
|
|
free(data);
|
|
fclose (in);
|
|
fclose(out_fd);
|
|
|
|
return 0;
|
|
}
|
|
|
|
// data is only 8 bytes long
|
|
static int AVI_read_data_fast(avi_t *AVI, char *buf, off_t *pos, off_t *len, off_t *key, char *data)
|
|
{
|
|
|
|
/*
|
|
* Return codes:
|
|
*
|
|
* 0 = reached EOF
|
|
* 1 = video data read
|
|
* 2 = audio data read from track 0
|
|
* 3 = audio data read from track 1
|
|
* 4 = audio data read from track 2
|
|
* ....
|
|
* 10 = traditional idx1 chunk
|
|
*/
|
|
|
|
off_t n;
|
|
int rlen;
|
|
*key=(off_t)0;
|
|
|
|
if(AVI->mode==AVI_MODE_WRITE) return 0;
|
|
|
|
while(1)
|
|
{
|
|
/* Read tag and length */
|
|
|
|
if( xio_read(AVI->fdes,data,8) != 8 ) return 0;
|
|
|
|
n = PAD_EVEN(str2ulong(data+4));
|
|
|
|
if(strncasecmp(data,"LIST",4) == 0 ||
|
|
strncasecmp(data,"RIFF",4) == 0) { // prevents skipping extended RIFF chunks
|
|
if( xio_read(AVI->fdes,data,4) != 4 ) return 0;
|
|
n -= 4;
|
|
// put here tags of lists that need to be looked into
|
|
if(strncasecmp(data,"movi",4) == 0 ||
|
|
strncasecmp(data,"rec ",4) == 0 ||
|
|
strncasecmp(data,"AVI ",4) == 0 ||
|
|
strncasecmp(data,"AVIX",4) == 0) {
|
|
// xio_lseek(AVI->fdes,-4,SEEK_CUR);
|
|
continue; // proceed to look into it
|
|
} // otherwise seek over it later on
|
|
}
|
|
|
|
// the following list of comparisons should not include list tags;
|
|
// these should all go in the list above
|
|
if(strncasecmp(data,"IDX1",4) == 0)
|
|
{
|
|
// deal with it to extract keyframe info
|
|
*len = str2ulong(data+4);
|
|
*pos = xio_lseek(AVI->fdes, 0, SEEK_CUR)-(off_t)8;
|
|
//fprintf (stderr, "Found an index chunk at %lld len %lld\n", *pos, *len);
|
|
if(xio_lseek(AVI->fdes,n,SEEK_CUR)==(off_t)-1) return 0;
|
|
return 10;
|
|
}
|
|
|
|
if(strncasecmp(data,AVI->video_tag,3) == 0)
|
|
{
|
|
*len = str2ulong(data+4);
|
|
*pos = xio_lseek(AVI->fdes, 0, SEEK_CUR)-(off_t)8;
|
|
AVI->video_pos++;
|
|
rlen = n;
|
|
xio_read(AVI->fdes, buf, rlen);
|
|
if(xio_lseek(AVI->fdes,n-rlen,SEEK_CUR)==(off_t)-1) return 0;
|
|
return 1;
|
|
}
|
|
else if(AVI->anum>=1 && strncasecmp(data,AVI->track[0].audio_tag,4) == 0)
|
|
{
|
|
*len = str2ulong(data+4);
|
|
*pos = xio_lseek(AVI->fdes, 0, SEEK_CUR)-(off_t)8;
|
|
AVI->track[0].audio_posc++;
|
|
rlen = (n<LEN)?n:LEN;
|
|
xio_read(AVI->fdes, buf, rlen);
|
|
if(xio_lseek(AVI->fdes,n-rlen,SEEK_CUR)==(off_t)-1) return 0;
|
|
return 2;
|
|
break;
|
|
}
|
|
else if(AVI->anum>=2 && strncasecmp(data,AVI->track[1].audio_tag,4) == 0)
|
|
{
|
|
*len = str2ulong(data+4);
|
|
*pos = xio_lseek(AVI->fdes, 0, SEEK_CUR)-(off_t)8;
|
|
AVI->track[1].audio_posc++;
|
|
rlen = (n<LEN)?n:LEN;
|
|
xio_read(AVI->fdes, buf, rlen);
|
|
if(xio_lseek(AVI->fdes,n-rlen,SEEK_CUR)==(off_t)-1) return 0;
|
|
return 3;
|
|
break;
|
|
}
|
|
else if(AVI->anum>=3 && strncasecmp(data,AVI->track[2].audio_tag,4) == 0)
|
|
{
|
|
*len = str2ulong(data+4);
|
|
*pos = xio_lseek(AVI->fdes, 0, SEEK_CUR)-(off_t)8;
|
|
AVI->track[2].audio_posc++;
|
|
rlen = (n<LEN)?n:LEN;
|
|
xio_read(AVI->fdes, buf, rlen);
|
|
if(xio_lseek(AVI->fdes,n-rlen,SEEK_CUR)==(off_t)-1) return 0;
|
|
return 4;
|
|
break;
|
|
}
|
|
else if(AVI->anum>=4 && strncasecmp(data,AVI->track[3].audio_tag,4) == 0)
|
|
{
|
|
*len = str2ulong(data+4);
|
|
*pos = xio_lseek(AVI->fdes, 0, SEEK_CUR)-(off_t)8;
|
|
AVI->track[3].audio_posc++;
|
|
rlen = (n<LEN)?n:LEN;
|
|
xio_read(AVI->fdes, buf, rlen);
|
|
if(xio_lseek(AVI->fdes,n-rlen,SEEK_CUR)==(off_t)-1) return 0;
|
|
return 5;
|
|
break;
|
|
}
|
|
else if(AVI->anum>=5 && strncasecmp(data,AVI->track[4].audio_tag,4) == 0)
|
|
{
|
|
*len = str2ulong(data+4);
|
|
*pos = xio_lseek(AVI->fdes, 0, SEEK_CUR)-(off_t)8;
|
|
AVI->track[4].audio_posc++;
|
|
rlen = (n<LEN)?n:LEN;
|
|
xio_read(AVI->fdes, buf, rlen);
|
|
if(xio_lseek(AVI->fdes,n-rlen,SEEK_CUR)==(off_t)-1) return 0;
|
|
return 6;
|
|
break;
|
|
}
|
|
else if(AVI->anum>=6 && strncasecmp(data,AVI->track[5].audio_tag,4) == 0)
|
|
{
|
|
*len = str2ulong(data+4);
|
|
*pos = xio_lseek(AVI->fdes, 0, SEEK_CUR)-(off_t)8;
|
|
AVI->track[5].audio_posc++;
|
|
rlen = (n<LEN)?n:LEN;
|
|
xio_read(AVI->fdes, buf, rlen);
|
|
if(xio_lseek(AVI->fdes,n-rlen,SEEK_CUR)==(off_t)-1) return 0;
|
|
return 7;
|
|
break;
|
|
}
|
|
else if(AVI->anum>=7 && strncasecmp(data,AVI->track[6].audio_tag,4) == 0)
|
|
{
|
|
*len = str2ulong(data+4);
|
|
*pos = xio_lseek(AVI->fdes, 0, SEEK_CUR)-(off_t)8;
|
|
AVI->track[6].audio_posc++;
|
|
rlen = (n<LEN)?n:LEN;
|
|
xio_read(AVI->fdes, buf, rlen);
|
|
if(xio_lseek(AVI->fdes,n-rlen,SEEK_CUR)==(off_t)-1) return 0;
|
|
return 8;
|
|
break;
|
|
}
|
|
else if(AVI->anum>=8 && strncasecmp(data,AVI->track[7].audio_tag,4) == 0)
|
|
{
|
|
*len = str2ulong(data+4);
|
|
*pos = xio_lseek(AVI->fdes, 0, SEEK_CUR)-(off_t)8;
|
|
AVI->track[7].audio_posc++;
|
|
rlen = (n<LEN)?n:LEN;
|
|
xio_read(AVI->fdes, buf, rlen);
|
|
if(xio_lseek(AVI->fdes,n-rlen,SEEK_CUR)==(off_t)-1) return 0;
|
|
return 9;
|
|
break;
|
|
} else {
|
|
if(xio_lseek(AVI->fdes,n,SEEK_CUR)==(off_t)-1) return 0;
|
|
}
|
|
// else if(xio_lseek(AVI->fdes,n,SEEK_CUR)==(off_t)-1) return 0;
|
|
}
|
|
}
|
|
|
|
static int is_key(unsigned char *data, long size, char *codec)
|
|
{
|
|
if (strncasecmp(codec, "div3", 4) == 0) {
|
|
|
|
int32_t c=( (data[0]<<24) | (data[1]<<16) | (data[2]<<8) | (data[3]&0xff) );
|
|
if(c&0x40000000) return(0);
|
|
else return 1;
|
|
|
|
} else if (strncasecmp(codec, "xvid", 4) == 0 || strncasecmp(codec, "divx", 4) == 0
|
|
|| strncasecmp(codec, "dx50", 4) == 0 || strncasecmp(codec, "div4", 4) == 0
|
|
|| strncasecmp(codec, "mpg4", 4) == 0) {
|
|
int result = 0;
|
|
int i;
|
|
|
|
for(i = 0; i < size - 5; i++)
|
|
{
|
|
if( data[i] == 0x00 && data[i + 1] == 0x00 &&
|
|
data[i + 2] == 0x01 && data[i + 3] == 0xb6) {
|
|
|
|
if((data[i + 4] & 0xc0) == 0x0) return 1;
|
|
else return 0;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
// mjpeg, uncompressed, etc
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
|
|
avi_t *avifile1=NULL;
|
|
|
|
char *in_file=NULL, *out_file=NULL;
|
|
|
|
long frames;
|
|
|
|
double fps;
|
|
|
|
int track_num=0, aud_tracks;
|
|
|
|
int ret;
|
|
long i=0, chunk=0;
|
|
|
|
int ch;
|
|
int progress=0, old_progress=0;
|
|
|
|
long rate;
|
|
int format, chan, bits;
|
|
int aud_bitrate = 0;
|
|
|
|
FILE *out_fd = NULL;
|
|
int open_without_index=0,index_keyframes=0;
|
|
int force_with_index=0;
|
|
|
|
double vid_ms = 0.0, print_ms = 0.0;
|
|
double aud_ms [ AVI_MAX_TRACKS ];
|
|
char tag[8];
|
|
char *data;
|
|
int vid_chunks=0, aud_chunks[AVI_MAX_TRACKS];
|
|
off_t pos, len, key=0, index_pos=0, index_len=0,size=0;
|
|
struct stat st;
|
|
char *codec;
|
|
int idx_type=0;
|
|
off_t ioff;
|
|
char fcclen[8]; // FOURCC + len
|
|
|
|
ftype_t ftype;
|
|
FILE *idxfile;
|
|
|
|
ac_init(AC_ALL);
|
|
|
|
if(argc==1) usage(EXIT_FAILURE);
|
|
|
|
for (i=0; i<AVI_MAX_TRACKS; i++) {
|
|
aud_chunks[i] = 0;
|
|
aud_ms[i] = 0;
|
|
}
|
|
|
|
while ((ch = getopt(argc, argv, "a:vi:o:nxf?h")) != -1)
|
|
{
|
|
|
|
switch (ch) {
|
|
|
|
case 'i':
|
|
|
|
if(optarg[0]=='-') usage(EXIT_FAILURE);
|
|
in_file=optarg;
|
|
|
|
break;
|
|
|
|
case 'a':
|
|
|
|
if(optarg[0]=='-') usage(EXIT_FAILURE);
|
|
track_num = atoi(optarg);
|
|
|
|
if(track_num<0) usage(EXIT_FAILURE);
|
|
|
|
break;
|
|
|
|
case 'o':
|
|
|
|
if(optarg[0]=='-') usage(EXIT_FAILURE);
|
|
out_file=optarg;
|
|
|
|
break;
|
|
|
|
case 'n':
|
|
|
|
open_without_index=1;
|
|
|
|
break;
|
|
|
|
case 'x':
|
|
|
|
open_without_index=1;
|
|
index_keyframes=1;
|
|
|
|
break;
|
|
|
|
case 'f':
|
|
force_with_index=1;
|
|
break;
|
|
|
|
case 'v':
|
|
version();
|
|
exit(0);
|
|
break;
|
|
case 'h':
|
|
usage(EXIT_SUCCESS);
|
|
default:
|
|
usage(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
// check
|
|
if(in_file==NULL) usage(EXIT_FAILURE);
|
|
if (!out_file) out_fd = stdout;
|
|
else out_fd = fopen(out_file, "w+r");
|
|
|
|
if (!out_fd) {
|
|
perror("ERROR cannot open outputfile");
|
|
exit(1);
|
|
}
|
|
|
|
idxfile = fopen(in_file, "r");
|
|
fread (fcclen, 8, 1, idxfile);
|
|
if (strncasecmp(fcclen, "RIFF", 4) == 0) ftype = RIFF;
|
|
else if (strncasecmp(fcclen, "AVIIDX1", 7) == 0) ftype = AVIIDX1;
|
|
else if (strncasecmp(fcclen, "MPIDX1", 6) == 0) ftype = MPIDX1;
|
|
else ftype = UNKNOWN;
|
|
|
|
fclose (idxfile);
|
|
|
|
switch (ftype) {
|
|
case RIFF: fprintf(stderr, "[%s] Seems to be an AVI file.\n", EXE); break;
|
|
case AVIIDX1: fprintf(stderr, "[%s] Converting a transcode to an mplayer index file.\n", EXE);
|
|
return aviidx1_to_mpidx1(in_file, out_fd);
|
|
case MPIDX1: fprintf(stderr, "[%s] Converting an mplayer to a transcode index file.\n", EXE);
|
|
return mpidx1_to_aviidx1(in_file, out_fd);
|
|
default: fprintf(stderr, "[%s] Unrecognized format\n", EXE); return (1);
|
|
}
|
|
|
|
|
|
// if file is larger than 2GB, regen index
|
|
|
|
if (stat(in_file, &st)<0) {
|
|
perror("Stat input file");
|
|
return 1;
|
|
}
|
|
|
|
size = st.st_size;
|
|
if (size > (off_t)AVI_MAX_LEN/2)
|
|
if (!force_with_index) open_without_index = 1;
|
|
|
|
if (open_without_index)
|
|
if (index_keyframes) fprintf(stderr, "[%s] Open \"%s\" without index and don't use index for keyframes info\n",EXE, in_file);
|
|
else fprintf(stderr, "[%s] Open \"%s\" without index but use index (if any) for keyframes info\n",EXE, in_file);
|
|
else
|
|
fprintf(stderr, "[%s] Open \"%s\" with index (fast)\n", EXE, in_file);
|
|
|
|
// header. Magic tag is AVIIDX1
|
|
fprintf(out_fd, "AVIIDX1 # Generated by %s (%s-%s)\n", EXE, PACKAGE, VERSION); // Magic
|
|
fprintf(out_fd, "TAG TYPE CHUNK CHUNK/TYPE POS LEN KEY MS\n");
|
|
|
|
data = malloc (5*1024*1204);
|
|
|
|
if (open_without_index) {
|
|
|
|
// open file with index.
|
|
if(NULL == (avifile1 = AVI_open_input_file(in_file,0))) {
|
|
AVI_print_error("AVI open input file");
|
|
exit(1);
|
|
}
|
|
|
|
aud_tracks = frames = 0;
|
|
frames = AVI_video_frames(avifile1);
|
|
fps = AVI_frame_rate (avifile1);
|
|
codec = AVI_video_compressor(avifile1);
|
|
|
|
aud_tracks = AVI_audio_tracks(avifile1);
|
|
//printf("frames (%ld), aud_tracks (%d)\n", frames, aud_tracks);
|
|
|
|
pos = key = len = (off_t)0;
|
|
i = 0;
|
|
|
|
while ( (ret = AVI_read_data_fast (avifile1, data, &pos, &len, &key, fcclen)) != 0) {
|
|
int audtr = ret-2;
|
|
|
|
/* don't need this and it saves time
|
|
* */
|
|
if (audtr>=0 && audtr<=7) {
|
|
AVI_set_audio_track(avifile1, audtr);
|
|
format = AVI_audio_format (avifile1);
|
|
chan = AVI_audio_channels(avifile1);
|
|
rate = AVI_audio_rate (avifile1);
|
|
bits = AVI_audio_bits (avifile1);
|
|
bits = bits==0?16:bits;
|
|
if (tc_format_ms_supported(format)) {
|
|
|
|
aud_bitrate = format==0x1?1:0;
|
|
|
|
if (!aud_bitrate && tc_get_audio_header(data, LEN, format, NULL, NULL, &aud_bitrate)<0) {
|
|
aud_ms[audtr] = vid_ms;
|
|
} else
|
|
aud_ms[audtr] += (len*8.0)/(format==0x1?((double)(rate*chan*bits)/1000.0):aud_bitrate);
|
|
}
|
|
}
|
|
|
|
switch (ret) {
|
|
case 1: ac_memcpy(tag, fcclen, 4);
|
|
print_ms = vid_ms = (avifile1->video_pos)*1000.0/fps;
|
|
chunk = avifile1->video_pos;
|
|
key = is_key(data, len, codec);
|
|
break;
|
|
case 2: case 3:
|
|
case 4: case 5:
|
|
case 6: case 7:
|
|
case 8:
|
|
case 9: ac_memcpy(tag, fcclen, 4);
|
|
print_ms = aud_ms[audtr];
|
|
chunk = avifile1->track[audtr].audio_posc;
|
|
break;
|
|
case 10: tc_snprintf(tag, sizeof(tag), "idx1");
|
|
index_pos = pos;
|
|
index_len = len;
|
|
print_ms = 0.0;
|
|
chunk = 0;
|
|
break;
|
|
|
|
case 0:
|
|
default:
|
|
// never get here
|
|
break;
|
|
}
|
|
|
|
|
|
//if (index_pos != pos)
|
|
// tag, chunk_nr
|
|
fprintf(out_fd, "%.4s %d %ld %ld %lld %lld %lld %.2f\n",
|
|
tag, ret, i, chunk-1,
|
|
(long long)pos, (long long)len, (long long)key,
|
|
print_ms);
|
|
i++;
|
|
|
|
// don't update the counter every chunk
|
|
progress = (int)(pos*100/size)+1;
|
|
if (old_progress != progress) {
|
|
fprintf(stderr, "[%s] Scanning ... %d%%\r", EXE, progress);
|
|
old_progress = progress;
|
|
}
|
|
|
|
}
|
|
fprintf(stderr, "\n");
|
|
|
|
// check if we have found an index chunk to restore keyframe info
|
|
if (!index_pos || !index_len || index_keyframes)
|
|
goto aviout;
|
|
|
|
fprintf(stderr, "[%s] Found an index chunk. Using it to regenerate keyframe info.\n", EXE);
|
|
fseek (out_fd, 0, SEEK_SET);
|
|
|
|
fgets(data, 100, out_fd); // magic
|
|
fgets(data, 100, out_fd); // comment
|
|
|
|
len = (off_t)0;
|
|
vid_chunks = 0;
|
|
|
|
xio_lseek(avifile1->fdes, index_pos+8, SEEK_SET);
|
|
while (len<index_len) {
|
|
xio_read(avifile1->fdes, tag, 8);
|
|
|
|
// if its a keyframe and is a video chunk
|
|
if (str2ulong(tag+4) && tag[1] == '0') {
|
|
int typen, keyn;
|
|
long chunkn, chunkptypen;
|
|
long long posn, lenn;
|
|
char tagn[5];
|
|
double msn=0.0;
|
|
|
|
chunk = (long)(len/16);
|
|
i = 0;
|
|
//fprintf(stderr, "keyframe in chunk %ld\n", chunk);
|
|
|
|
// find line "chunk" in the logfile
|
|
|
|
while (i<chunk-vid_chunks) {
|
|
fgets(data, 100, out_fd);
|
|
i++;
|
|
}
|
|
|
|
vid_chunks += (chunk-vid_chunks);
|
|
posn = ftell(out_fd);
|
|
fgets(data, 100, out_fd);
|
|
fseek(out_fd, posn, SEEK_SET);
|
|
sscanf(data, "%s %d %ld %ld %lld %lld %d %lf",
|
|
tagn, &typen, &chunkn, &chunkptypen, &posn, &lenn, &keyn, &msn);
|
|
fprintf(out_fd, "%s %d %ld %ld %lld %lld %d %.2f",
|
|
tagn, typen, chunkn, chunkptypen, posn, lenn, 1, msn);
|
|
}
|
|
|
|
xio_lseek(avifile1->fdes, 8, SEEK_CUR);
|
|
len += 16;
|
|
}
|
|
|
|
|
|
|
|
} else { // with index
|
|
|
|
// open file with index.
|
|
if(NULL == (avifile1 = AVI_open_input_file(in_file,1))) {
|
|
AVI_print_error("AVI open input file");
|
|
exit(1);
|
|
}
|
|
i=0;
|
|
|
|
AVI_info(avifile1);
|
|
|
|
// idx1 contains only info for first chunk of opendml AVI
|
|
if(avifile1->idx && !avifile1->is_opendml)
|
|
{
|
|
off_t pos, len;
|
|
|
|
/* Search the first videoframe in the idx1 and look where
|
|
it is in the file */
|
|
|
|
for(i=0;i<avifile1->n_idx;i++)
|
|
if( strncasecmp(avifile1->idx[i],avifile1->video_tag,3)==0 ) break;
|
|
|
|
pos = str2ulong(avifile1->idx[i]+ 8);
|
|
len = str2ulong(avifile1->idx[i]+12);
|
|
|
|
xio_lseek(avifile1->fdes,pos,SEEK_SET);
|
|
if(xio_read(avifile1->fdes,data,8)!=8) return 1;
|
|
if( strncasecmp(data,avifile1->idx[i],4)==0 && str2ulong(data+4)==len )
|
|
{
|
|
idx_type = 1; /* Index from start of file */
|
|
}
|
|
else
|
|
{
|
|
xio_lseek(avifile1->fdes,pos+avifile1->movi_start-4,SEEK_SET);
|
|
if(xio_read(avifile1->fdes,data,8)!=8) return 1;
|
|
if( strncasecmp(data,avifile1->idx[i],4)==0 && str2ulong(data+4)==len )
|
|
{
|
|
idx_type = 2; /* Index from start of movi list */
|
|
}
|
|
}
|
|
/* idx_type remains 0 if neither of the two tests above succeeds */
|
|
|
|
|
|
ioff = idx_type == 1 ? 0 : avifile1->movi_start-4;
|
|
//fprintf(stderr, "index type (%d), ioff (%ld)\n", idx_type, (long)ioff);
|
|
i=0;
|
|
|
|
//printf("nr idx: %d\n", avifile1->n_idx);
|
|
while (i<avifile1->n_idx) {
|
|
ac_memcpy(tag, avifile1->idx[i], 4);
|
|
// tag
|
|
fprintf(out_fd, "%c%c%c%c",
|
|
avifile1->idx[i][0], avifile1->idx[i][1],
|
|
avifile1->idx[i][2], avifile1->idx[i][3]);
|
|
|
|
// type, absolute chunk number
|
|
fprintf(out_fd, " %c %ld", avifile1->idx[i][1]+1, i);
|
|
|
|
|
|
switch (avifile1->idx[i][1]) {
|
|
case '0':
|
|
fprintf(out_fd, " %d", vid_chunks);
|
|
vid_chunks++;
|
|
break;
|
|
case '1': case '2':
|
|
case '3': case '4':
|
|
case '5': case '6':
|
|
case '7': case '8':
|
|
// uhoh
|
|
ret = avifile1->idx[i][1]-'0';
|
|
fprintf(out_fd, " %d", aud_chunks[ret]);
|
|
aud_chunks[ret]++;
|
|
break;
|
|
default:
|
|
fprintf(out_fd, " %d", -1);
|
|
break;
|
|
}
|
|
|
|
pos = str2ulong(avifile1->idx[i]+ 8);
|
|
pos += ioff;
|
|
// pos
|
|
fprintf(out_fd, " %llu", (unsigned long long)pos);
|
|
// len
|
|
fprintf(out_fd, " %lu", str2ulong(avifile1->idx[i]+12));
|
|
// flags (keyframe?);
|
|
fprintf(out_fd, " %d", (str2ulong(avifile1->idx[i]+ 4))?1:0);
|
|
|
|
// ms (not available here)
|
|
fprintf(out_fd, " %.2f", 0.0);
|
|
|
|
fprintf(out_fd, "\n");
|
|
|
|
i++;
|
|
}
|
|
}
|
|
|
|
else
|
|
{ // try to extract from the index that AVILIB built,
|
|
// possibly from OpenDML superindex
|
|
|
|
long aud_entry [ AVI_MAX_TRACKS ] = { 0 };
|
|
long vid_entry = 0;
|
|
char* tagp;
|
|
|
|
off_t pos, len = 0;
|
|
i = chunk = 0;
|
|
|
|
|
|
while (1) {
|
|
ret = pos = 0;
|
|
int j = 0;
|
|
|
|
if(vid_entry < avifile1->video_frames) {
|
|
pos = avifile1->video_index[vid_entry].pos;
|
|
len = avifile1->video_index[vid_entry].len;
|
|
key = (avifile1->video_index[vid_entry].key) & 16 ? 1 : 0;
|
|
chunk = vid_entry;
|
|
ret = 1;
|
|
}
|
|
for(j = 0; j < AVI_audio_tracks(avifile1); ++j) {
|
|
if(aud_entry[j] < avifile1->track[j].audio_chunks) {
|
|
if(!ret || avifile1->track[j].audio_index[aud_entry[j]].pos < pos) {
|
|
pos = avifile1->track[j].audio_index[aud_entry[j]].pos;
|
|
len = avifile1->track[j].audio_index[aud_entry[j]].len;
|
|
key = 0;
|
|
chunk = aud_entry[j];
|
|
ret = j + 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!ret) // end of all index streams
|
|
break;
|
|
|
|
if (ret == 1)
|
|
{
|
|
++vid_entry;
|
|
tagp = avifile1->video_tag;
|
|
}
|
|
else
|
|
{
|
|
aud_entry[ret-2]++;
|
|
tagp = avifile1->track[ret-2].audio_tag;
|
|
}
|
|
|
|
// index points to data in chunk, but chunk offset is needed here
|
|
pos -= 8;
|
|
fprintf(out_fd, "%.4s %d %ld %ld %lld %lld %lld %.2f\n",
|
|
tagp, ret, i, chunk,
|
|
(long long)pos, (long long)len, (long long)key,
|
|
0.0);
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
aviout:
|
|
free(data);
|
|
if (out_fd!=stdout) fclose (out_fd);
|
|
AVI_close(avifile1);
|
|
|
|
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:
|
|
*/
|