123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737 |
- /*
- ** nsvlib.cpp - NSV file/bitstream reading/writing code
- **
- ** Copyright (C) 2001-2002 Nullsoft, Inc.
- **
- ** Confidential Subject to NDA
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <bfc/platform/strcmp.h>
- #include "nsvlib.h"
- #define NSV_HDR_DWORD (NSV_MAKETYPE('N','S','V','f'))
- #define NSV_SYNC_HEADERLEN_BITS 192
- #define NSV_SYNC_DWORD (NSV_MAKETYPE('N','S','V','s'))
- #define NSV_NONSYNC_HEADERLEN_BITS 56
- #define NSV_NONSYNC_WORD 0xBEEF
- #define NSV_INVALID_SYNC_OFFSET 0x80000000
- long glSyncFrameCount = 0l;
- long glCounterNSVf = 0l;
- long glNonSyncFrameCount = 0l;
- /*
- NSV sync packet header
- 32 bits: NSV_SYNC_DWORD
- 32 bits: video format
- 32 bits: audio format
- 16 bits: width
- 16 bits: height
- 8 bits: framerate (see getfrate/setfrate)
- 16 bits: audio/video sync offset
- or
- NSV nonsync packet header
- 16 bits: NSV_NONSYNC_WORD
- then
- 4 bits: # aux data channels present (max 15)
- 20 bits: video data + aux channels length
- 16 bits: audio data length
- --------------------------------
- sync:
- 192 bit header,
- 136 bits are invariant
- nonsync:
- 56 bit header
- 16 bits are invariant
- */
- static int is_type_char_valid(int c)
- {
- c&=0xff;
- return (c >= 'a' && c <= 'z') ||
- (c >= 'A' && c <= 'Z') ||
- (c >= '0' && c <= '9') ||
- c == ' ' || c == '-' ||
- c == '.' || c == '_';
- }
- static int is_type_valid(unsigned int t)
- {
- return (t&0xff) != ' ' &&
- is_type_char_valid(t>>24) &&
- is_type_char_valid(t>>16) &&
- is_type_char_valid(t>>8) &&
- is_type_char_valid(t);
- }
- void nsv_type_to_string(unsigned int t, char *out)
- {
- if (is_type_valid(t))
- {
- out[0]=(t)&0xff;
- out[1]=(t>>8)&0xff;
- out[2]=(t>>16)&0xff;
- out[3]=(t>>24)&0xff;
- out[4]=0;
- int x=3;
- while (out[x]==' ' && x > 0) out[x--]=0;
- }
- else *out=0;
- }
- unsigned int nsv_string_to_type(char *in)
- {
- int n;
- unsigned int ret=*in;
- if (*in == ' ' || !is_type_char_valid(*in)) return 0;
- in++;
- for (n = 0; n < 3; n ++)
- {
- if (!is_type_char_valid(*in)) break;
- ret|=(*in<<(8+8*n));
- in++;
- }
- if (*in) return 0;
- return ret;
- }
- // frate is specified
- // XYYYYYZZ
- // if !X, framerate is YYYYYZZ (1-127)
- // otherwise:
- // ZZ indexes base
- // YYYYY is scale (0-32).
- // if YYYYY < 16, then scale = 1/(YYYY+1)
- // otherwise scale = YYYYY-15
- static double frate2double(unsigned char fr)
- {
- static double fratetab[]=
- {
- 30.0,
- 30.0*1000.0/1001.0,
- 25.0,
- 24.0*1000.0/1001.0,
- };
- if (!(fr&0x80)) return (double)fr;
- double sc;
- int d=(fr&0x7f)>>2;
- if (d < 16) sc=1.0/(double)(d+1);
- else sc=d-15;
- return fratetab[fr&3]*sc;
- }
- static unsigned char double2frate(double fr)
- {
- int best=0;
- double best_v=1000000.0;
- int x;
- for (x = 0; x < 256; x ++)
- {
- double this_v=(fr-frate2double(x));
- if (this_v<0) this_v=-this_v;
- if (this_v < best_v)
- {
- best_v=this_v;
- best=x;
- }
- }
- return (unsigned char) best;
- }
- nsv_Packeter::nsv_Packeter()
- {
- vidfmt=audfmt=0;
- width=height=0;
- framerate_idx=0;
- framerate=0.0;
- syncoffset_cur=0;
- video=NULL;
- audio=NULL;
- video_len=0;
- audio_len=0;
- aux_used=0;
- }
- void nsv_Packeter::setVidFmt(unsigned int vfmt, unsigned int w, unsigned int h, double frt)
- {
- vidfmt=vfmt;
- width=w;
- height=h;
- framerate=frt;
- framerate_idx=double2frate(frt);
- }
- nsv_Packeter::~nsv_Packeter()
- {
- }
- int nsv_Packeter::packet(nsv_OutBS &bs)
- {
- int total_auxlen=0;
- int x;
- if (width >= (1<<16) || height >= (1<<16) ||
- !framerate_idx || framerate_idx > 255 ||
- !is_type_valid(audfmt) ||
- !is_type_valid(vidfmt) ||
- video_len > NSV_MAX_VIDEO_LEN ||
- audio_len > NSV_MAX_AUDIO_LEN ||
- aux_used > NSV_MAX_AUXSTREAMS ||
- aux_used < 0
- ) return -1;
- for (x = 0; x < aux_used; x ++)
- {
- if (aux_len[x] > NSV_MAX_AUX_LEN) return -1;
- total_auxlen+=aux_len[x]+6;
- }
-
- if (is_sync_frame)
- {
- bs.putbits(32,NSV_SYNC_DWORD);
- bs.putbits(32,vidfmt);
- bs.putbits(32,audfmt);
- bs.putbits(16,width);
- bs.putbits(16,height);
- bs.putbits(8 ,framerate_idx);
- bs.putbits(16,syncoffset_cur);
- }
- else
- {
- bs.putbits(16,NSV_NONSYNC_WORD);
- }
-
- bs.putbits(4,aux_used); // no aux data channels for our streams yet
- bs.putbits(20,video_len+total_auxlen);
- bs.putbits(16,audio_len);
- for (x = 0; x < aux_used; x ++)
- {
- bs.putbits(16,aux_len[x]); // length of 0 for aux channels
- bs.putbits(32,aux_types[x]);
- if (aux_len[x]) bs.putdata(aux_len[x]*8,aux[x]);
- }
- if (video_len) bs.putdata(video_len*8,video);
- if (audio_len) bs.putdata(audio_len*8,audio);
- return 0;
- }
- void nsv_Unpacketer::reset(int full)
- {
- synched=0;
- is_sync_frame=0;
- syncoffset_cur=0;
- syncoffset=NSV_INVALID_SYNC_OFFSET;
- if (full)
- {
- m_auxbs=NULL;
- m_audiobs=NULL;
- m_videobs=NULL;
- m_eof=0;
- vidfmt=0;
- audfmt=0;
- valid=0;
- width=0;
- height=0;
- framerate=0.0;
- framerate_idx=0;
- }
- }
- // returns 0 on success, >0 on needs (at least X bytes) more data,
- // -1 on error (no header found in block)
- int nsv_Unpacketer::unpacket(nsv_InBS &bs)
- {
- int gotframe=0;
- unsigned int num_aux=0;
- unsigned int vl=0;
- unsigned int al=0;
-
- while (bs.avail()>=NSV_NONSYNC_HEADERLEN_BITS)
- {
- if (valid && synched)
- {
- if (bs.avail() < NSV_NONSYNC_HEADERLEN_BITS)
- return m_eof?-1:(NSV_NONSYNC_HEADERLEN_BITS- (int)(bs.avail())/8);
- unsigned int d=bs.getbits(16);
- if (d == NSV_NONSYNC_WORD)
- {
- glNonSyncFrameCount++;
- num_aux=bs.getbits(4);
- vl=bs.getbits(20);
- al=bs.getbits(16);
- if (al >= NSV_MAX_AUDIO_LEN ||
- vl >= (NSV_MAX_VIDEO_LEN+num_aux*(NSV_MAX_AUX_LEN+6)))
- {
- bs.seek(-NSV_NONSYNC_HEADERLEN_BITS);
- }
- else
- {
- if ((unsigned int)bs.avail() < 8*(vl+al)+(m_eof?0:32))
- {
- int l=(al+vl+32/8)- (int)(bs.avail()/8);
- bs.seek(-NSV_NONSYNC_HEADERLEN_BITS);
- return m_eof?-1:l;
- }
- if ((unsigned int)bs.avail() >= 8*(vl+al)+32)
- {
- bs.seek(8*(vl+al));
- unsigned int a32=bs.getbits(32);
- bs.seek(-32);
- unsigned int a16=bs.getbits(16);
- bs.seek(-16);
- bs.seek(-8*(vl+al));
- if (a16 != NSV_NONSYNC_WORD && a32 != NSV_SYNC_DWORD)
- {
- bs.seek(-NSV_NONSYNC_HEADERLEN_BITS);
- }
- else gotframe=NSV_NONSYNC_HEADERLEN_BITS;
- }
- else gotframe=NSV_NONSYNC_HEADERLEN_BITS;
- }
- }
- else bs.seek(-16);
- } // inf.valid && inf.synched
- // gotframe is set if we successfully got a nonsync frame, otherwise
- // let's see if we can't interpret this as a sync frame
- if (!gotframe)
- {
- if (bs.avail() < NSV_SYNC_HEADERLEN_BITS)
- return (int)(m_eof?-1:(NSV_SYNC_HEADERLEN_BITS-(bs.avail())/8));
- unsigned int d=bs.getbits(32);
- if (d != NSV_SYNC_DWORD)
- {
- bs.seek(8-32); // seek back 3 bytes
- synched=0;
- continue;
- }else{
- // count the # of sync frames (for debugging)
- glSyncFrameCount++;
- }
- unsigned int vfmt=bs.getbits(32);
- unsigned int afmt=bs.getbits(32);
- unsigned int w=bs.getbits(16);
- unsigned int h=bs.getbits(16);
- unsigned char frt=bs.getbits(8);
- unsigned int so=bs.getbits(16);
- num_aux=bs.getbits(4);
- vl=bs.getbits(20);
- al=bs.getbits(16);
- if (al >= NSV_MAX_AUDIO_LEN ||
- vl >= (NSV_MAX_VIDEO_LEN+num_aux*(NSV_MAX_AUX_LEN+6)) ||
- !frt || !is_type_valid(vfmt) || !is_type_valid(afmt) ||
- (valid &&
- (width != w || height != h ||
- vidfmt != vfmt || audfmt != afmt || framerate_idx != frt)))
- { // frame is definately not valid
- bs.seek(8-NSV_SYNC_HEADERLEN_BITS); // seek back what we just read
- synched=0;
- continue;
- }
-
- if ((unsigned int)bs.avail() < (al+vl)*8+((m_eof||(valid&&synched))?0:32))
- {
- int l=(al+vl)*8+NSV_SYNC_HEADERLEN_BITS- (int)(bs.avail());
- bs.seek(-NSV_SYNC_HEADERLEN_BITS);
- return m_eof?-1:(l/8);
- }
- if (valid && synched)
- {
- gotframe=NSV_SYNC_HEADERLEN_BITS;
- }
- else // we need to do more robust sync
- {
- int sk=(al+vl)*8;
- bs.seek(sk);
- unsigned int a16=bs.getbits(16);
- bs.seek(-16);
- unsigned int a32=bs.getbits(32);
- bs.seek(-32);
- if (a16 == NSV_NONSYNC_WORD)
- {
- sk+=16+4+20+16;
- bs.seek(16);
- unsigned int _num_aux=bs.getbits(4);
- unsigned int _vl=bs.getbits(20);
- unsigned int _al=bs.getbits(16);
- if ((unsigned int)bs.avail() < (_vl+_al)*8 + 32)
- {
- int l=(_al+_vl+32)- (int)(bs.avail()/8);
- bs.seek(-NSV_SYNC_HEADERLEN_BITS-sk);
- return m_eof?-1:l;
- }
- bs.seek((_vl+_al)*8);
- sk+=(_vl+_al)*8;
- unsigned int a16=bs.getbits(16);
- bs.seek(-16);
- unsigned int a32=bs.getbits(32);
- bs.seek(-32);
- bs.seek(-sk);
- if (a16 == NSV_NONSYNC_WORD || a32 == NSV_SYNC_DWORD)
- gotframe=NSV_SYNC_HEADERLEN_BITS;
- }
- else if (a32 == NSV_SYNC_DWORD)
- {
- glSyncFrameCount++;
-
- sk+=32+32+32+16+16+8;
-
- bs.seek(32);
- unsigned int _vfmt=bs.getbits(32);
- unsigned int _afmt=bs.getbits(32);
- unsigned int _w=bs.getbits(16);
- unsigned int _h=bs.getbits(16);
- unsigned char _frt=bs.getbits(8);
- bs.seek(-sk);
- if (_vfmt==vfmt && _afmt==afmt && _w==w && _h==h && _frt==frt) // matches
- {
- gotframe=NSV_SYNC_HEADERLEN_BITS;
- }
- }
- }
- if (!gotframe)
- {
- synched=0;
- bs.seek(8-NSV_SYNC_HEADERLEN_BITS);
- }
- else
- {
- if (so & 0x8000) so|=0xFFFF0000;
- syncoffset_cur=so;
- if (!valid || syncoffset == NSV_INVALID_SYNC_OFFSET) syncoffset=so;
- if (!valid) framerate=frate2double(frt);
- framerate_idx=frt;
- width=w;
- height=h;
- audfmt=afmt;
- vidfmt=vfmt;
- valid=1;
- synched=1;
- }
- }
- if (gotframe)
- {
- is_sync_frame = (gotframe == NSV_SYNC_HEADERLEN_BITS);
- // read aux channels
- int rd=gotframe;
- unsigned int x;
- for (x = 0; x < num_aux; x ++)
- {
- unsigned int l=bs.getbits(16);
- unsigned int fmt=bs.getbits(32);
- vl -= 4+2;
- rd += 16+32;
- if (l > NSV_MAX_AUX_LEN) break;
- if (m_auxbs)
- {
- m_auxbs->addint(l);
- m_auxbs->addint(fmt);
- m_auxbs->add(bs.getcurbyteptr(),l);
- }
- bs.seek(l*8); // toss aux
- vl-=l;
- rd+=l*8;
- if (vl<0) break; // invalid frame (aux channels add up to more than video)
- }
- if (x < num_aux) // oh shit, invalid frame
- {
- synched=0;
- bs.seek(8-rd);
- gotframe=0;
- continue;
- }
-
- if (m_videobs)
- {
- m_videobs->addint(vl);
- m_videobs->add(bs.getcurbyteptr(),vl);
- }
- bs.seek(vl*8);
- if (m_audiobs)
- {
- m_audiobs->addint(al);
- m_audiobs->add(bs.getcurbyteptr(),al);
- }
- bs.seek(al*8);
- return 0;
- }
- } // while
- return m_eof?-1:(NSV_NONSYNC_HEADERLEN_BITS- (int)(bs.avail())/8);
- }
- /* NSV file header
- 4: NSV_HDR_DWORD
- 4: length of header in bytes
- -- may not be 0 or 0xFFFFFFFF. :)
- 4: length of file, in bytes (including header - if this is 0 we are invalid)
- -- can be 0xFFFFFFFF which means unknown length
- 4: length of file, in milliseconds (max file length, 24 days or so)
- -- can be 0xFFFFFFFF which means unknown length
- 4: metadata length
- 4: number of TOC entries allocated
- 4: number of TOC entries used
- mdlen: metadata
- TOC_alloc*4:offset in file at time t.
- */
- void nsv_writeheader(nsv_OutBS &bs, nsv_fileHeader *hdr, unsigned int padto)
- {
- if (hdr->toc_alloc < hdr->toc_size)
- hdr->toc_alloc=hdr->toc_size;
- if (hdr->toc_ex && hdr->toc_alloc <= hdr->toc_size*2)
- hdr->toc_alloc=hdr->toc_size*2+1;
- hdr->header_size = 4+4+4+4+4+hdr->metadata_len+4+4+4*hdr->toc_alloc;
- bs.putbits(32,NSV_HDR_DWORD);
- bs.putbits(32,hdr->header_size>padto?hdr->header_size:padto);
- if (hdr->file_lenbytes == 0xFFFFFFFF) bs.putbits(32,hdr->file_lenbytes);
- else bs.putbits(32,hdr->file_lenbytes+(hdr->header_size>padto?hdr->header_size:padto));
- bs.putbits(32,hdr->file_lenms);
- bs.putbits(32,hdr->metadata_len);
- bs.putbits(32,hdr->toc_alloc);
- bs.putbits(32,hdr->toc_size);
- bs.putdata(hdr->metadata_len*8,hdr->metadata);
- unsigned int numtoc=hdr->toc_alloc;
- unsigned int numtocused=hdr->toc_size;
- unsigned int *toc=hdr->toc;
- unsigned int *toc_ex=hdr->toc_ex;
- unsigned int numtocused2=(toc_ex && hdr->toc_alloc > hdr->toc_size*2) ? (hdr->toc_size + 1): 0;
- while (numtoc--)
- {
- if (!numtocused)
- {
- if (numtocused2)
- {
- if (--numtocused2 == hdr->toc_size) // signal extended TOC :)
- bs.putbits(32,NSV_MAKETYPE('T','O','C','2'));
- else
- bs.putbits(32,*toc_ex++);
- }
- else // extra (unused by this implementation but could be used someday so we fill it with 0xFF) space
- bs.putbits(32,~0);
- }
- else if (toc)
- {
- bs.putbits(32,*toc++);
- numtocused--;
- }
- else bs.putbits(32,0);
- }
-
- unsigned int x;
- for (x = hdr->header_size; x < padto; x ++) bs.putbits(8,0);
- }
- int nsv_readheader(nsv_InBS &bs, nsv_fileHeader *hdr)
- {
- int s=0;
- hdr->metadata=(void*)NULL;
- hdr->toc=(unsigned int *)NULL;
- hdr->toc_ex=(unsigned int *)NULL;
- hdr->header_size=0;
- hdr->file_lenbytes=~0;
- hdr->file_lenms=~0;
- hdr->toc_alloc=0;
- hdr->toc_size=0;
- hdr->metadata_len=0;
-
- if (bs.avail()<64) {
- return 8- (int)(bs.avail()/8);
- }
- s+=32;
- if (bs.getbits(32) != NSV_HDR_DWORD)
- {
- bs.seek(-s);
- return -1;
- }else{
- glCounterNSVf++;
- }
- s+=32;
- unsigned int headersize=bs.getbits(32);
- if (headersize >= 0x20000000)
- {
- bs.seek(-s);
- return -1;
- }
- if ((unsigned int)bs.avail() < (headersize-4)*8)
- {
- int l=headersize-4- (int)(bs.avail()/8);
- bs.seek(-s);
- return l;
- }
- s+=32;
- unsigned int lenbytes=bs.getbits(32);
- s+=32;
- unsigned int lenms=bs.getbits(32);
- s+=32;
- unsigned int metadatalen=bs.getbits(32);
- s+=32;
- unsigned int tocalloc=bs.getbits(32);
- s+=32;
- unsigned int tocsize=bs.getbits(32);
- if (tocalloc < tocsize || lenbytes < headersize || tocalloc + metadatalen + s/8 > headersize)
- {
- bs.seek(-s);
- return -1;
- }
- void *metadata=NULL;
- if (metadatalen)
- {
- if (metadatalen > (SIZE_MAX/8))
- {
- bs.seek(-s);
- return -1;
- }
- metadata=malloc(metadatalen+1);
- if (!metadata)
- {
- bs.seek(-s);
- return -1;
- }
- s+=metadatalen*8;
- bs.getdata(metadatalen*8,metadata);
- ((char*)metadata)[metadatalen]=0;
- }
- unsigned int *toc=NULL;
- unsigned int *toc_ex=NULL;
- if (tocalloc && tocsize < (SIZE_MAX/8))
- {
- toc=(unsigned int *)malloc(tocsize * 4 * 2);
- if (!toc)
- {
- free(metadata);
- bs.seek(-s);
- return -1;
- }
- unsigned int x;
- int bitsread=0;
- for (x = 0; x < tocsize; x ++) { toc[x] = bs.getbits(32); bitsread += 32; }
- if (tocalloc > tocsize*2)
- {
- bitsread += 32;
- if (bs.getbits(32) == NSV_MAKETYPE('T','O','C','2'))
- {
- toc_ex=toc + tocsize;
- for (x = 0; x < tocsize; x ++) { toc_ex[x] = bs.getbits(32); bitsread += 32; }
- }
- }
- bs.seek((tocalloc-tocsize)*32 - bitsread);
- s+=tocalloc*32;
- }
- hdr->header_size=headersize;
- if (lenbytes == 0xFFFFFFFF)
- hdr->file_lenbytes=lenbytes;
- else
- hdr->file_lenbytes=lenbytes-headersize;
- hdr->file_lenms=lenms;
- hdr->metadata=metadata;
- hdr->metadata_len=metadatalen;
- hdr->toc=toc;
- hdr->toc_ex=toc_ex;
- hdr->toc_alloc=tocalloc;
- hdr->toc_size=tocsize;
-
- return 0;
- }
- char *nsv_getmetadata(void *metadata, char *name)
- {
- if (!metadata) return NULL;
- char *p=(char*)metadata;
- size_t ln=strlen(name);
- for (;;)
- {
- while (p && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')) p++;
- if (p || !*p) break;
- if (!_strnicmp(p,name,ln) && p[ln]=='=' && p[ln+1] && p[ln+2])
- {
- int cnt=0;
- char *np=p+ln+1;
- char c=*np++;
- while (np[cnt] && np[cnt] != c) cnt++;
- char *s=(char*)malloc(cnt+1);
- if (!s) return NULL;
- memcpy(s,np,cnt);
- s[cnt]=0;
- return s;
- }
- // advance to next item
- while (p && *p && *p != '=') p++;
- if (!*p++) break;
- if (!*p) break;
- char c=*p++;
- while (p && *p && *p != c) p++;
- if (*p) p++;
- }
- return NULL;
- }
|