/** \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; }
//______________________________________ // // 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; }