/**
      \fn mkvAudio 
*/
 mkvAudio::mkvAudio(const char *name,mkvTrak *track,mkvIndex *clust,uint32_t nbClusters) 
  : AVDMGenericAudioStream()
{
  _nbClusters=nbClusters;
  _clusters=clust;
  ADM_assert(_clusters);
  _destroyable = 1;
  
   _parser=new ADM_ebml_file();
   ADM_assert(_parser->open(name));
  _track=track; 
  ADM_assert(_track);
  
  
  // Compute total length in byte
  _length=_track->_sizeInBytes; // FIXME
  
  float f=_track->wavHeader.frequency;
  f*=_track->_defaultFrameDuration;
  f=f/1000000.;
  
  _frameDurationInSample=(uint32_t)floor(f+0.5);
  
  _wavheader=new WAVHeader;
  memcpy(_wavheader,&(_track->wavHeader),sizeof(WAVHeader));
  printf("[MKVAUDIO] found %lu bytes, %u samples per frame\n",_length,_frameDurationInSample);
  _currentLace=_maxLace=0;
  _clusterParser=NULL;
  goToCluster(0);
  /* In case of AC3, do not trust the header...*/
  if(_wavheader->encoding==WAV_AC3)
  {
    uint8_t ac3Buffer[20000];
    uint32_t len,sample,timecode;
     if( getPacket(ac3Buffer, &len, &sample,&timecode))
     {
       uint32_t fq,br,chan,syncoff;
        if( ADM_AC3GetInfo(ac3Buffer, len, &fq, &br, &chan,&syncoff) )
        {
            _wavheader->channels=chan;
            _wavheader->frequency=fq;
            _wavheader->byterate=br;
        }
     }
     goToCluster(0);
  }
}
uint8_t AVDMAC3AudioStream::open(char *name)
{
uint8_t syncbuff[10*1024];
uint32_t fq,br,l,chan;
uint32_t syncoff;


    fd = fopen(name, "rb");
    if (!fd)
		return 0;
	l=fread(syncbuff,10*1024,1,fd);
 	if(!l)
  		{
        	abort();
         	return 0;
    	}

    // read wavheader
    _wavheader = new WAVHeader;
    ADM_assert(_wavheader);
    if(! 		ADM_AC3GetInfo(syncbuff, 10*1024,&fq, &br,&chan,&syncoff)   )
    		{
            	abort();
             	printf("\n could not sync ac3!\n");
             	return 0;
        	}
    // else fill up wav header
    _wavheader->encoding = WAV_AC3;
    _wavheader->channels = chan;
    _wavheader->frequency = fq;
    _wavheader->bitspersample = 16;	// yes i know
    _wavheader->byterate = br;
    _wavheader->blockalign = 4;
    // now update length field
    //_________________________
    fseek(fd, 0, SEEK_END);
    _length = ftell(fd);
    //
    //

       _codec=getAudioCodec(WAV_AC3,_wavheader);
     ADM_assert(_codec);
    return 1;

}
uint8_t dmxAudioStream::probeAudio (void)
{
uint32_t read,offset,offset2,fq,br,chan,myPes,blocksize;          
uint8_t buffer[PROBE_SIZE];
MpegAudioInfo mpegInfo;
WAVHeader *hdr;

      for(int i=0;i<nbTrack;i++)
      {
        hdr=&(_tracks[i].wavHeader);
        // Change demuxer...

        demuxer->changePid(_tracks[i].myPid,_tracks[i].myPes);
        demuxer->setPos(0,0);
        printf("Probing track:%d, pid: %x pes:%x\n",i,_tracks[i].myPid,_tracks[i].myPes);
         //
        if(PROBE_SIZE!=(blocksize=demuxer->read(buffer,PROBE_SIZE)))
        {
           printf("DmxAudio: Reading for track %d failed (%u/%u)\n",i,blocksize,PROBE_SIZE);
           return 0;
        }
        myPes=_tracks[i].myPes;
        // Try mp2/3
        if(myPes>=0xC0 && myPes<=0xC9)
        {
                if(getMpegFrameInfo(buffer,PROBE_SIZE,&mpegInfo,NULL,&offset))
                {
                        if(getMpegFrameInfo(buffer+offset,PROBE_SIZE-offset,&mpegInfo,NULL,&offset2))
                                if(!offset2)
                                {
                                        hdr->byterate=(1000*mpegInfo.bitrate)>>3;
                                        hdr->frequency=mpegInfo.samplerate;

                                        if(mpegInfo.mode!=3) hdr->channels=2;
                                        else hdr->channels=1;

                                        if(mpegInfo.layer==3) hdr->encoding=WAV_MP3;
                                        else hdr->encoding=WAV_MP2;
                                        continue;
                                }
                }
        }
        // Try AC3
        if(myPes<9)
        {
                if(ADM_AC3GetInfo(buffer,PROBE_SIZE,&fq,&br,&chan,&offset))
                {
                        if(ADM_AC3GetInfo(buffer+offset,PROBE_SIZE-offset,&fq,&br,&chan,&offset2))
                        {
                                hdr->byterate=br; //(1000*br)>>3;
                                hdr->frequency=fq;                                
                                hdr->encoding=WAV_AC3;
                                hdr->channels=chan;
                                continue;
                        }
                }
        }

        if(myPes<=0x49 && myPes>=0x40)
        {
             
              uint32_t flags,samplerate,bitrate,framelength,syncoff,chan,nbs;
//int ADM_DCAGetInfo(uint8_t *buf, uint32_t len, uint32_t *fq, uint32_t *br, uint32_t *chan,uint32_t *syncoff,uint32_t *flags);
              if(ADM_DCAGetInfo(buffer,PROBE_SIZE,&samplerate,&bitrate,&chan,&syncoff,&flags,&nbs))
              {
                                hdr->byterate=bitrate/8;
                                hdr->frequency=samplerate;
                                hdr->encoding=WAV_DTS;
                                hdr->channels=chan;
                                continue;
               }
        }
        // Default 48khz stereo lpcm
        if(myPes>=0xA0 && myPes<0xA9)
        {
                hdr->byterate=(48000*4);
                hdr->frequency=48000;                                
                hdr->encoding=WAV_LPCM;
                hdr->channels=2;
                continue;
        }
         //AAC, can happen in TS file with H264
        if(myPes>=0xB0 && myPes<0xB9)
        {
          AacAudioInfo info;
          if(!getAACFrameInfo(buffer,blocksize, &info,NULL,&offset))
          {
            printf("\n Cannot get AAC sync info (not ADTS ?)\n");
                hdr->byterate=(128000)>>3;
                hdr->frequency=44100;
                hdr->encoding=WAV_AAC;
                hdr->channels=2;
                continue;
          }
/**
    \fn addAudioTrack
    \brief gather information about audio & add audio track to the list

*/
bool addAudioTrack(int pid, listOfPsAudioTracks *list, psPacketLinearTracker *p)
{

uint8_t audioBuffer[PROBE_ANALYZE_SIZE];
        uint64_t pts,dts,startAt;
        uint32_t packetSize;

        //
        int masked=pid&0xF0;
        if(masked!=MP2_AUDIO_VALUE &&  // MP2
            masked!=LPCM_AUDIO_VALUE && // PCM
            masked!=DTS_AC3_AUDIO_VALUE  // AC3 & DTS
            ) 
        {
            ADM_info("Not a type we know %x\n",(int)masked);
            return false;
        }

        // Go back where we were
        p->changePid(pid);
        p->getPacketOfType(pid,PROBE_ANALYZE_SIZE, &packetSize,&pts,&dts,audioBuffer,&startAt);
        //Realign
        p->seek(startAt,0);
        int rd=packetSize*2;
        if(rd>PROBE_ANALYZE_SIZE) rd=PROBE_ANALYZE_SIZE;
        if(!p->read(rd,audioBuffer))
        {
            ADM_info("Cannot read %d bytes of type %x\n",packetSize*2,pid);
            return false;
        }
        psAudioTrackInfo *info=new psAudioTrackInfo;
        info->esID=pid;
        uint32_t fq,br,chan,off;
        switch(pid & 0xF0)
        {
            case LPCM_AUDIO_VALUE: // LPCM
                            info->header.frequency=48000;
                            info->header.channels=2;
                            info->header.byterate=48000*4;
                            info->header.encoding=WAV_LPCM;
                            break;
            case MP2_AUDIO_VALUE: // MP2
                            {
                                if(! psCheckMp2Audio(&(info->header),audioBuffer,rd))
                                {
                                    ADM_warning("[PsProbeAudio] Failed to get info on track :%x (MP2)\n",pid);
                                    goto er;
                                }
                            }
                            break;
            case DTS_AC3_AUDIO_VALUE: // AC3 or DTS
                            if(pid>=0x8) // DTS
                            {
                                info->header.encoding=WAV_DTS;
                                ADM_DCA_INFO dcainfo;

                                if(false==ADM_DCAGetInfo(audioBuffer, rd, &dcainfo,&off))
                                {
                                        ADM_warning("[PsProbeAudio] Failed to get info on track :%x\n",pid);
                                        goto er;
                                }
                                info->header.frequency=dcainfo.frequency;
                                info->header.channels=dcainfo.channels;
                                info->header.byterate=dcainfo.bitrate/8;
                                break;
                            }else // AC3
                            {
                                info->header.encoding=WAV_AC3;
                                if(!ADM_AC3GetInfo(audioBuffer, rd, &fq, &br, &chan,&off))
                                {
                                        ADM_warning("[PsProbeAudio] Failed to get info on track :%x\n",pid);
                                        goto er;
                                }
                                info->header.frequency=fq;
                                info->header.channels=chan;
                                info->header.byterate=(br);
                                break;
                            }

            default:
                        ADM_assert(0);

        }
        list->append(info);
        return true;
er:
        delete info;
        return false;
}
示例#5
0
//______________________________________
//
// Open and recursively read the atoms
// until we got the information we want
// i.e. :
//	index for audio and video track
//	esds for mpeg4
//	size / codec used
//
// We don't care about sync atom and all
// other stuff which are pretty useless on
// 3gp file anyway.
//______________________________________
uint8_t    MP4Header::open(const char *name)
{
        printf("** opening 3gpp files **");
        _fd=ADM_fopen(name,"rb");
        if(!_fd)
        {
                printf("\n cannot open %s \n",name);
                return 0;
        }
#define CLR(x)              memset(& x,0,sizeof(  x));

        CLR( _videostream);
        CLR(  _mainaviheader);

        _videostream.dwScale=1000;
        _videostream.dwRate=10000;
        _mainaviheader.dwMicroSecPerFrame=100000;;     // 10 fps hard coded

        adm_atom *atom=new adm_atom(_fd);
        // Some mp4/mov files have the data at the end but do start properly
        // detect and workaround...
        // Check it is not mdat start(ADM_memcpy_0)
        uint8_t check[4];
        uint64_t fileSize;
        fseeko(_fd,0,SEEK_END);
        fileSize=ftello(_fd);
        fseeko(_fd,4,SEEK_SET);
        fread(check,4,1,_fd);
        fseeko(_fd,0,SEEK_SET);
        if(check[0]=='m' && check[1]=='d' &&check[2]=='a' && check[3]=='t')
        {
                        uint64_t of;
                        uint64_t hi,lo;
                                        printf("Data first, header later...\n");
                                        of=atom->read32();
                                        if(of==1)
                                        {
                                          atom->read32();	// size
                                          atom->read32();	// fcc
                                          hi=atom->read32();
                                          lo=atom->read32();
                                          of=(hi<<32)+lo;
                                          if(of>fileSize) of=hi;
                                        }
                                        fseeko(_fd,of,SEEK_SET);
                                        printf("Header starts at %"PRIx64"\n",of);
                                        delete atom;
                                        atom=new adm_atom(_fd);
        }
        //**************

        if(!lookupMainAtoms((void*) atom))
        {
          printf("Cannot find needed atom\n");
          fclose(_fd);
          _fd=NULL;
		  delete atom;
          return 0;
        }

        delete atom;

	      _isvideopresent=1;
	      _isaudiopresent=0;

              _videostream.fccType=fourCC::get((uint8_t *)"vids");
              _video_bih.biBitCount=24;
              _videostream.dwInitialFrames= 0;
              _videostream.dwStart= 0;

	printf("\n");


        if(!VDEO.index)
        {
                printf("No index!\n");
                return 0;
        }

        // If it is mpeg4 and we have extra data
        // Decode vol header to get the real width/height
        // The mpeg4/3GP/Mov header is often misleading

        if(fourCC::check(_videostream.fccHandler,(uint8_t *)"DIVX"))
        {
            if(VDEO.extraDataSize)
            {
                uint32_t w,h,ti;
                if(extractMpeg4Info(VDEO.extraData,VDEO.extraDataSize,&w,&h,&ti))
                {
                    printf("MP4 Corrected size : %"PRIu32" x %"PRIu32"\n",w,h);
                    _video_bih.biWidth=_mainaviheader.dwWidth=w ;
                    _video_bih.biHeight=_mainaviheader.dwHeight=h;
                }
            }else { printf("No extradata to probe\n");}

        }
        else
        {

        /*
            Same story for H263 : Analyze 1st frame to get the real width/height
        */
            if(fourCC::check(_videostream.fccHandler,(uint8_t *)"H263"))
            {
                uint32_t w,h,sz;
                uint8_t *bfer=NULL;
                sz=VDEO.index[0].size;
                if(sz)
                {
                        bfer=new uint8_t[sz];
                        ADMCompressedImage img;
                        img.data=bfer;
                        if(getFrame(0,&img))
                        {
                        if(extractH263Info(bfer,sz,&w,&h))
                        {
                            printf("H263 Corrected size : %"PRIu32" x %"PRIu32"\n",w,h);
                            _video_bih.biWidth=_mainaviheader.dwWidth=w ;
                            _video_bih.biHeight=_mainaviheader.dwHeight=h;
                        }
                        else
                        {
                                  printf("H263 COULD NOT EXTRACT SIZE, using : %"PRIu32" x %"PRIu32"\n",
                                      _video_bih.biWidth,  _video_bih.biHeight);
                        }
                        }
                        delete [] bfer;
                }
            }
        }
        /*
         * Veryfy DTS<=PTS
         */
        int nb=(int)_tracks[0].nbIndex;
        uint64_t delta,maxDelta=0;
        for(int i=0;i<nb;i++)
        {
            uint64_t pts,dts;
            dts=VDEO.index[i].dts;
            pts=VDEO.index[i].pts;
            if(pts==ADM_COMPRESSED_NO_PTS || dts==ADM_COMPRESSED_NO_PTS) continue;
            if(dts>=pts)
            {
                uint64_t delta=(uint64_t)(dts-pts);
                if(delta>maxDelta) maxDelta=delta;
            }
        }
        if(maxDelta)
        {
            shiftTimeBy(maxDelta);
             _movieDuration+=(maxDelta+999)/1000;
        }
        /*
                Now build audio tracks
        */
        if(nbAudioTrack) _isaudiopresent=1; // Still needed ?
        
        adjustElstDelay();
        
        //
        for(int audio=0;audio<nbAudioTrack;audio++)
        {
            switch(_tracks[1+audio]._rdWav.encoding)
            {
                
            // Lookup if AAC is lying about # of channels
            case WAV_AAC:
                {
                    if(_tracks[1+audio].extraDataSize==2)
                    {
                        // Channels
                        uint32_t word=(_tracks[1+audio].extraData[0]<<8)+_tracks[1+audio].extraData[1];
                        uint32_t chan=(word>>3)&0xf;
                        uint32_t fqIndex=(word>>7)&0xf;
                        printf("0x%x word, Channel : %d, fqIndex=%d\n",word,chan,fqIndex);
                    }
                }
                break;
            case WAV_AC3: // same for ac3
            {
                 // read First chunk
                
                MP4Index *dex=_tracks[1+audio].index;
                int size=dex[0].size;
                uint8_t *buffer=new uint8_t[size];
                  fseeko(_fd,dex[0].offset,SEEK_SET);
                  if(fread(buffer,1,size,_fd))
                  {
                      uint32_t fq,  br,  chan, syncoff;
                      if(ADM_AC3GetInfo(buffer,size, &fq, &br, &chan,&syncoff))
                      {
                          ADM_info("Updating AC3 info : Fq=%d, br=%d, chan=%d\n",fq,br,chan);
                          _tracks[1+audio]._rdWav.channels=chan;
                          _tracks[1+audio]._rdWav.byterate=br;
                      }
                  }
                  delete [] buffer;
            }
                break;
            default:
                break;
            }
            audioAccess[audio]=new ADM_mp4AudioAccess(name,&(_tracks[1+audio]));
            audioStream[audio]=ADM_audioCreateStream(&(_tracks[1+audio]._rdWav), audioAccess[audio]);
        }
uint8_t dmx_probePS(char *file,  uint32_t *nbTracks,MPEG_TRACK **tracks)
{
uint8_t dummy[10];
uint64_t seen[256],abs,rel;
int     audio,video;

        // It is mpeg PS
        // Create a fake demuxer, set a probe limite and collect info for all streams found
        dmx_demuxerPS *demuxer;
        MPEG_TRACK    pseudo;

               pseudo.pes=0xea; // Hopefully not used
               pseudo.pid=0;
               demuxer=new dmx_demuxerPS(256,&pseudo,0);
               if(!demuxer->open(file))
                {
                        delete demuxer;
                        printf("Cannot open file file demuxer (%s)\n",file);
                        return 0;
                }
                DIA_StartBusy();
                demuxer->setProbeSize(MAX_PROBE);
                demuxer->read(dummy,1);
                demuxer->getStats(seen);
                demuxer->getPos( &abs,&rel);
                //abs>>=20;
                printf("Stopped at %"LLU", %"LLU" MB\n",abs,abs>>20);

                DIA_StopBusy();
        // Now analyze...
        video=0;
        // Take the first video track suitable
        for(int i=0xE0;i<0xE9;i++)
        {
                if(seen[i]>MIN_DETECT)
                {
                        video=i;
                        break;
                }
        }
        if(!video)
        {
                printf("Cannot find any video stream\n");
                delete demuxer;
                return 0;
        }
        
        audio=0;
#if 1
        for(int i=0;i<256;i++)
        {
                if(seen[i]) printf("%x: there is something %lu kb\n",i,seen[i]>>10);
        }
#endif
        // 1 count how much audio we have
        for(int i=0;i<9;i++) if(seen[i]>MIN_DETECT) audio++;
        for(int i=0xc0;i<0xc9;i++) if(seen[i]>MIN_DETECT) audio++;
        for(int i=0xA0;i<0xA9;i++) if(seen[i]>MIN_DETECT) audio++;

        *nbTracks=audio+1;      
        *tracks=new MPEG_TRACK[*nbTracks];
        
        memset(*tracks,0,(audio+1)*sizeof(MPEG_TRACK));
        (*tracks)[0].pes=video;
        audio=1;
#define DOME {(*tracks)[audio].pes=i;audio++;}
        for(int i=0;i<9;i++) if(seen[i]>MIN_DETECT) DOME;
        for(int i=0xc0;i<0xc9;i++) if(seen[i]>MIN_DETECT) DOME;
        for(int i=0xA0;i<0xA9;i++) if(seen[i]>MIN_DETECT) DOME;

        // Now go a bit deeper and try to extract infos
        #define BUFFER_SIZE (10*1024)

        uint8_t buffer[BUFFER_SIZE];
        uint32_t read;
        uint32_t br,fq,offset,pes,chan;
        MpegAudioInfo mpegInfo;        

        for(int i=1;i<audio;i++)
        {

                pes=(*tracks)[i].pes;
                if((pes<0xC9 && pes>=0xc0) || ((pes<9)))
                {
                        demuxer->changePid(0,pes);
                        demuxer->setPos(0,0);
                        read=demuxer->read(buffer,BUFFER_SIZE);
                        // We need about 5 Ko...
                        if(read>BUFFER_SIZE>>1)
                        {
                                if(pes<9)
                                {
                                        if(ADM_AC3GetInfo(buffer,read,&fq,&br,&chan,&offset))
                                        {
                                                (*tracks)[i].channels=chan;
                                                (*tracks)[i].bitrate=(8*br)/1000;
                                        }
                                }else
                                {
                                        if(getMpegFrameInfo(buffer,read,&mpegInfo,NULL,&offset))
                                        {
                                                if(mpegInfo.mode!=3)  (*tracks)[i].channels=2;
                                                         else  (*tracks)[i].channels=1;
                                                
                                                (*tracks)[i].bitrate=mpegInfo.bitrate;
                                        }
                                }                

                        }
                }

        }
//________________________________________________________________
//      Go to a beginning of an audio frame after the offset given
//  Offset can be not frame bounded (MP3), correct that.
//_________________________________________________________________
uint8_t AVDMGenericAudioStream::goToSync(uint32_t offset)
{
    static uint8_t a,b, c;
    #define SYNCAC3 8192
    uint8_t	syncbuff[SYNCAC3];
    uint32_t suboffset = 0,byterate;
    uint32_t tryz;
    
    assert(_wavheader);
    // can't deal with something not mp3...
  //  printf("syncing asked : %lu",offset);
    switch(  _wavheader->encoding )
    {

    		case WAV_AC3:
					{
					  uint32_t fq,br,chan;
					  if(!goTo(offset)) return 0;
					  if(SYNCAC3!=read(SYNCAC3,syncbuff))
					  {
					  	printf("Cannot read enough bytes!\n");
						return 0;					  
					  }
					
					  if(!ADM_AC3GetInfo(syncbuff, SYNCAC3,&fq, &br, &chan,&suboffset))
					  {
					  	return 0;
					  }
					  if(!goTo(offset+suboffset)) return 0;
					  printf("A52 sync found at %lu + %lu\n",offset,suboffset);		
					  }
					  return 1;
					  break;
		case WAV_WMA:
					uint32_t wmaoffset;
					if(_wavheader->blockalign)
						wmaoffset=offset - (offset%_wavheader->blockalign);
					else 
						wmaoffset=offset;
		      			printf("... wma offset : %lu",wmaoffset);
				    	if(!goTo(wmaoffset)) return 0;
					return 1;
		case WAV_MP4:
		#warning FIXME NEED DOC
				suboffset=0;
				tryz=0;
				if(!goTo(offset)) return 0;
				while(tryz<1200)
				{
					if (!read(1, &a))
					{
						printf("MP4sync :Read failed\n");
	      					return 0;
					}
					if(a==0x21)
						{
							if (!read(1, &a))
	      							return 0;
							if(a==0x0a)
							{
								if(!goTo(offset+suboffset))
								{
									printf("MP4sync :Seek failed\n");
									return 0;
								}
								printf("MP4sync : sync %ld\n",suboffset);
								return 1;
							}
							suboffset++;
						}
					suboffset++;
					tryz++;
				}
				printf("MP4sync : cound not find sync\n");
				return 0;
						
      		case WAV_PCM:
      			uint32_t mask;

         		mask=0xffffffff;
           	switch( _wavheader->channels)
            	{
               		case 1:
                			mask=0xfffffffe; // just even
                   		break;
                  	case 2:
                  		mask=0xfffffffc;   // 1100
                    		break;
                   	default:
                   		mask=0xffffffff;
                     		printf("\n more than 2 channels on PCM ????\n");
                }
				return goTo(mask & offset);
    			break;
       case WAV_MP3:
       case WAV_MP2:
    //
    // in case of MP3, we do know how to search sync frame...
    if(!goTo(offset)) return 0;
    // search sync
    do
      {
_doitagain:      
	  if (!read(1, &a))
	      return 0;		//EOF
	  suboffset++;
	  if (a == 0xff)
	    {
rebranch2:
		if (!read(1, &c))
		    return 0;
		suboffset++;

		switch (c)
		  {
		  case 0xff:
		      goto rebranch2;
		  case 0xfa:
		  case 0xfb:
         	  case 0xfc:
         	  case 0xfd:
   		  case 0xf3:
	     	  case 0xf5:
		  	// For mpeg1/2 layer 1/2 (svcd/VCD/DVD)
			// We make some extra check to avoid cutting
			// on a falsely detected mpeg frame that would
			// make the muxer barfs
		  	if(_wavheader->encoding==WAV_MP2 && 1 && _mpegSync[0])
			{
				if (!read(1, &a))
			    		return 0;
				suboffset++;
				if(a==0xff) goto rebranch2;
			
    				 if (!read(1, &b))
			    		return 0;
				suboffset++;
				if(b==0xff) goto rebranch2;
				if(c==_mpegSync[0]&& a==_mpegSync[1] && b==_mpegSync[2])
				{
					suboffset-=2;
					goto contact2;
				}
				// Failed
				printf("False header, continuing..\n");
				continue;
         		}
		      goto contact2;
		  }
	    }
      }
    while (1);
  contact2:
    printf("\n audio suboffset :%lu", suboffset - 2);
     printf("\n total offset :%lu", offset +suboffset - 2);
    return goTo(offset + suboffset - 2);
    break;

    default:
    		return goTo(offset);
      		break;
      }
      assert(0);
      return 0;
}