/* * avi.c: AVI container functions for replex * * * Copyright (C) 2003 Marcus Metzler * Metzler Brothers Systementwicklung GbR * * This program 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 * of the License, or (at your option) any later version. * * * This program 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 * General Public License for more details. * * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * Or, point your browser to http://www.gnu.org/copyleft/gpl.html * */ #include #include #include "mpg_common.h" #include "avi.h" #include "replex.h" #include "pes.h" #define DEBUG 1 #ifdef DEBUG #include "mpg_common.h" #endif uint32_t getle32(uint8_t *buf) { return (buf[3]<<24)|(buf[2]<<16)|(buf[1]<<8)|buf[0]; } uint32_t getbe32(uint8_t *buf) { return (buf[0]<<24)|(buf[1]<<16)|(buf[2]<<8)|buf[3]; } void printhead(uint8_t *buf) { printf("%c%c%c%c ", buf[0], buf[1], buf[2], buf[3]); } uint32_t getsize(int fd) { int len; uint8_t buf[4]; len=read(fd, buf, 4); return getle32(buf); } uint32_t getsize_buf(int8_t *buf) { return getle32(buf); } int check_riff(avi_context *ac, uint8_t *buf, int len) { uint32_t tag; int c = 0; if (len < 12) return -1; tag = getle32(buf); if (tag != TAG_IT('R','I','F','F')) return -1; c+=4; ac->riff_end = getle32(buf+c); c+=4; tag = getle32(buf+c); if (tag != TAG_IT('A','V','I',' ') && tag != TAG_IT('A','V','I','X') ) return -1; return c+4; } static int new_idx_frame( avi_context *ac, uint32_t pos, uint32_t len, uint32_t fl, uint32_t id) { int num = ac->num_idx_frames; if (ac->num_idx_alloc < num+1){ avi_index *idx; uint32_t newnum = num + 1024; if (ac->idx){ idx = realloc(ac->idx, newnum*sizeof(avi_index)); } else { idx = malloc(newnum*sizeof(avi_index)); } if (!idx) return -1; ac->idx = idx; ac->num_idx_alloc = newnum; } ac->idx[num].off = pos; ac->idx[num].id = id; ac->idx[num].len = len; ac->idx[num].flags = fl; ac->num_idx_frames++; return 0; } void print_index(avi_context *ac, int num){ char *cc; cc = (char *) &ac->idx[num].id; fprintf(stderr,"%d chunkid: %c%c%c%c ", num, *cc,*(cc+1),*(cc+2),*(cc+3)); fprintf(stderr," chunkoff: 0x%04x ", (int)ac->idx[num].off); fprintf(stderr," chunksize: 0x%04x ", ac->idx[num].len); fprintf(stderr," chunkflags: 0x%04x \n", ac->idx[num].flags); } int avi_read_index(avi_context *ac, int fd) { uint32_t tag; uint32_t isize; uint32_t c; off_t start; uint8_t buf[16]; char *cc; if (!(ac->avih_flags & AVI_HASINDEX)) return -2; fprintf(stderr,"READING INDEX\n"); start = lseek(fd, 0, SEEK_CUR); lseek(fd, ac->movi_length+ac->movi_start+4, SEEK_SET); read(fd,buf,4); tag = getle32(buf); if (tag != TAG_IT('i','d','x','1')){ cc = (char *) &tag; fprintf(stderr," tag: %c%c%c%c\n ",*cc, *(cc+1),*(cc+2),*(cc+3)); lseek(fd, start, SEEK_SET); return -1; } isize = getsize(fd); c = 0; while ( c < isize ){ uint32_t chunkid; uint32_t chunkflags; uint32_t chunkoff; uint32_t chunksize; read(fd,buf,16); chunkid = getle32(buf); chunkflags = getle32(buf+4); chunkoff = getle32(buf+8); chunksize = getle32(buf+12); new_idx_frame(ac, chunkoff, chunksize, chunkflags,chunkid); switch(chunkid){ case TAG_IT('0','1','w','b'): ac->achunks++; if (!chunksize) ac->zero_achunks++; break; case TAG_IT('0','0','d','c'): ac->vchunks++; if (!chunksize) ac->zero_vchunks++; break; } #ifdef DEBUG /* print_index(ac,ac->num_idx_frames-1); */ #endif c+=16; } #ifdef DEBUG fprintf(stderr,"Found %d video (%d were empty) and %d audio (%d were empty) chunks\n", (int)ac->vchunks, (int)ac->zero_vchunks, (int)ac->achunks, (int)ac->zero_achunks); #endif lseek(fd, start, SEEK_SET); return 0; } int read_avi_header( avi_context *ac, int fd) { uint8_t buf[256]; uint32_t tag; uint32_t size = 0; int c = 0; int skip=0; int n; #ifdef DEBUG char *cc; #endif while ((c=read(fd, buf, 4))==4) { skip=0; tag = getle32(buf); #ifdef DEBUG cc = (char *) &tag; fprintf(stderr,"tag: %c%c%c%c ",*cc,*(cc+1),*(cc+2),*(cc+3)); #endif switch(tag){ case TAG_IT('L','I','S','T'): size = getsize(fd); break; case TAG_IT('m','o','v','i'): ac->done=1; ac->movi_start = lseek(fd, 0, SEEK_CUR); ac->movi_length = size-8; #ifdef DEBUG fprintf(stderr," size: %d",size); fprintf(stderr," header done\n"); #endif return 0; break; case TAG_IT('h','d','r','l'): break; case TAG_IT('s','t','r','l'): break; case TAG_IT('J','U','N','K'): case TAG_IT('s','t','r','f'): case TAG_IT('s','t','r','d'): case TAG_IT('s','t','r','n'): size = getsize(fd); skip=1; break; case TAG_IT('a','v','i','h'): size = getsize(fd); c=0; read(fd,buf,size); ac->msec_per_frame = getle32(buf+c); c+=12; ac->avih_flags = getle32(buf+c); c+=4; ac->total_frames = getle32(buf+c); c+=4; ac->init_frames = getle32(buf+c); c+=4; ac->nstreams = getle32(buf+c); c+=8; ac->width = getle32(buf+c); c+=4; ac->height = getle32(buf+c); c+=4; #ifdef DEBUG fprintf(stderr," size: %d\n",size); fprintf(stderr," microsecs per frame %d\n", ac->msec_per_frame); if (ac->avih_flags & AVI_HASINDEX) fprintf(stderr," AVI has index\n"); if (ac->avih_flags & AVI_USEINDEX) fprintf(stderr," AVI must use index\n"); if (ac->avih_flags & AVI_INTERLEAVED) fprintf(stderr," AVI is interleaved\n"); if(ac->total_frames) fprintf(stderr," total frames: %d\n", ac->total_frames); fprintf(stderr," number of streams: %d\n", ac->nstreams); fprintf(stderr," size: %dx%d\n", ac->width, ac->height); #endif break; case TAG_IT('s','t','r','h'): size = getsize(fd); #ifdef DEBUG fprintf(stderr," size: %d\n",size); #endif c=0; read(fd,buf,size); tag = getle32(buf); c+=16; #ifdef DEBUG cc = (char *) &tag; fprintf(stderr," tag: %c%c%c%c ",*cc, *(cc+1),*(cc+2),*(cc+3)); #endif switch ( tag ){ case TAG_IT('v','i','d','s'): ac->vhandler = getle32(buf+4); #ifdef DEBUG if (ac->vhandler){ cc = (char *) &ac->vhandler; fprintf(stderr," video handler: %c%c%c%c " ,*cc,*(cc+1),*(cc+2),*(cc+3)); } #endif ac->vi.initial_frames = getle32(buf+c); c+=4; ac->vi.dw_scale = getle32(buf+c); c+=4; ac->vi.dw_rate = getle32(buf+c); c+=4; ac->vi.dw_start = getle32(buf+c); c+=4; if (ac->vi.dw_scale) ac->vi.fps = (ac->vi.dw_rate*1000)/ ac->vi.dw_scale; #ifdef DEBUG fprintf(stderr,"\n"); #endif fprintf(stderr,"AVI video info: "); fprintf(stderr,"dw_scale %d dw_rate %d ", ac->vi.dw_scale, ac->vi.dw_rate); fprintf(stderr,"fps %0.3f ini_frames %d dw_start %d\n", ac->vi.fps/1000.0, ac->vi.initial_frames, ac->vi.dw_start); break; case TAG_IT('a','u','d','s'): ac->ahandler = getle32(buf+4); #ifdef DEBUG if (ac->vhandler){ cc = (char *) &ac->ahandler; fprintf(stderr," audio handler: %c%c%c%c " ,*cc,*(cc+1),*(cc+2),*(cc+3)); } #endif if (ac->ntracks == MAX_TRACK) break; n = ac->ntracks; ac->ai[n].initial_frames = getle32(buf+c); c+=4; ac->ai[n].dw_scale = getle32(buf+c); c+=4; ac->ai[n].dw_rate = getle32(buf+c); c+=4; ac->ai[n].dw_start = getle32(buf+c); c+=16; ac->ai[n].dw_ssize = getle32(buf+c); if (ac->ai[n].dw_scale) ac->ai[n].fps = (ac->ai[n].dw_rate*1000)/ ac->ai[n].dw_scale; #ifdef DEBUG fprintf(stderr,"\n"); #endif fprintf(stderr,"AVI audio%d info: ",n); fprintf(stderr,"dw_scale %d dw_rate %d ", ac->ai[n].dw_scale, ac->ai[n].dw_rate); fprintf(stderr,"ini_frames %d dw_start %d", ac->ai[n].initial_frames, ac->ai[n].dw_start); fprintf(stderr," fps %0.3f sam_size %d\n", ac->ai[n].fps/1000., ac->ai[n].dw_ssize); ac->ntracks++; break; } break; case TAG_IT('I','N','F','O'): size -=4; skip =1; #ifdef DEBUG fprintf(stderr," size: %d",size); #endif break; } #ifdef DEBUG fprintf(stderr,"\n"); #endif if (skip){ lseek(fd, size, SEEK_CUR); size = 0; } } return -1; } #define MAX_BUF_SIZE 0xffff int get_avi_from_index(pes_in_t *p, int fd, avi_context *ac, void (*func)(pes_in_t *p), int insize) { struct replex *rx= (struct replex *) p->priv; avi_index *idx = ac->idx; int cidx = ac->current_idx; uint8_t buf[MAX_BUF_SIZE]; uint32_t cid; int c=0; off_t pos=0; int per = 0; static int lastper=0; if (cidx > ac->num_idx_frames) return -2; switch(idx[cidx].id){ case TAG_IT('0','1','w','b'): p->type = 1; p->rbuf = &rx->arbuffer[0]; break; case TAG_IT('0','0','d','c'): p->type = 0xE0; p->rbuf = &rx->vrbuffer; break; default: fprintf(stderr,"strange chunk :\n"); show_buf((uint8_t *) &idx[cidx].id,4); fprintf(stderr,"offset: 0x%04x length: 0x%04x\n", (int)idx[cidx].off, (int)idx[cidx].len); ac->current_idx++; p->found=0; return 0; break; } memset(buf, 0, MAX_BUF_SIZE); pos=lseek (fd, idx[cidx].off+ac->movi_start-4, SEEK_SET); read(fd,buf,idx[cidx].len); cid = getle32(buf); c+=4; p->plength = getsize_buf(buf+c); // show_buf(buf,16); if (idx[cidx].len > insize) return 0; if (idx[cidx].len > MAX_BUF_SIZE){ fprintf(stderr,"Buffer too small in get_avi_from_index\n"); exit(1); } if (!idx[cidx].len){ func(p); ac->current_idx++; p->found=0; return 0; } if (cid != idx[cidx].id){ char *cc; cc = (char *)&idx[cidx].id; fprintf(stderr,"wrong chunk id: %c%c%c%c != %c%c%c%c\n", buf[0],buf[1],buf[2],buf[3] ,*cc,*(cc+1),*(cc+2),*(cc+3)); print_index(ac,cidx); exit(1); } if (p->plength != idx[cidx].len){ fprintf(stderr,"wrong chunk size: %d != %d\n", (int)p->plength, idx[cidx].len); exit(1); } c+=4; p->done = 1; p->ini_pos = ring_wpos(p->rbuf); per = (int)(100*(pos-ac->movi_start)/ac->movi_length); if (per>lastper) fprintf(stderr,"read %3d%%\r", per); lastper = per; if (ring_write(p->rbuf, buf+c, p->plength)<0){ fprintf(stderr, "ring buffer overflow %d 0x%02x\n" ,p->rbuf->size,p->type); exit(1); } func(p); init_pes_in(p, 0, NULL, p->withbuf); ac->current_idx++; return 0; } void get_avi(pes_in_t *p, uint8_t *buf, int count, void (*func)(pes_in_t *p)) { int l; int c=0; struct replex *rx= (struct replex *) p->priv; // show_buf(buf,16); while (c < count && p->found < 8 && !p->done){ switch ( p->found ){ case 0: if (buf[c] == '0') p->found++; else p->found = 0; c++; break; case 1: if (buf[c] == '0'|| buf[c] == '1'){ p->found++; p->which = buf[c] - '0'; } else if (buf[c] == '0'){ p->found = 1; } else p->found = 0; c++; break; case 2: switch(buf[c]){ case 'w': case 'd': p->found++; p->type=buf[c]; break; default: p->found = 0; break; } c++; break; case 3: switch(buf[c]){ case 'b': if (p->type == 'w'){ p->found++; p->type = 1; } else p->found = 0; break; case 'c': if (p->type == 'd'){ p->found++; p->type = 0xE0; } else p->found = 0; break; default: p->found = 0; break; } switch(p->type){ case 1: p->rbuf = &rx->arbuffer[0]; break; case 0xE0: p->rbuf = &rx->vrbuffer; break; } c++; break; case 4: p->plen[0] = buf[c]; c++; p->found++; break; case 5: p->plen[1] = buf[c]; c++; p->found++; break; case 6: p->plen[2] = buf[c]; c++; p->found++; break; case 7: p->plen[3] = buf[c]; c++; p->found++; p->plength = getsize_buf(p->plen); if (!p->plength){ func(p); p->found=0; break; } p->done = 1; p->ini_pos = ring_wpos(p->rbuf); /* if (p->type == 1) fprintf(stderr,"audio 0x%x 0x%x\n", p->plength,ALIGN(p->plength)); if (p->type == 1) fprintf(stderr,"video 0x%x 0x%x\n", p->plength,ALIGN(p->plength)); */ break; default: break; } } if (p->done || p->found > 8){ while (c < count && p->found < p->plength+8){ l = count -c; if (l+p->found > p->plength+8) l = p->plength+8-p->found; if (ring_write(p->rbuf, buf+c, l)<0){ fprintf(stderr, "ring buffer overflow %d\n" ,p->rbuf->size); exit(1); } p->found += l; c += l; } if(p->found == p->plength+8){ func(p); } } if (p->plength && p->found == p->plength+8) { int a = 0;//ALIGN(p->plength); init_pes_in(p, 0, NULL, p->withbuf); if (c+a < count) get_avi(p, buf+c+a, count-c-a, func); } }