import{Demuxer as o}from"../demuxer.js";import{InputAudioTrack as h}from"../input-track.js";import{assert as s,AsyncMutex as l,binarySearchExact as u,binarySearchLessOrEqual as c,UNDETERMINED_LANGUAGE as p}from"../misc.js";import{EncodedPacket as f,PLACEHOLDER_DATA as x}from"../packet.js";import{getXingOffset as g,INFO as S,XING as P}from"../../shared/mp3-misc.js";import{Mp3Reader as k}from"./mp3-reader.js";export class Mp3Demuxer extends o{constructor(e){super(e),this.metadataPromise=null,this.firstFrameHeader=null,this.loadedSamples=[],this.tracks=[],this.loadingMutex=new l,this.lastLoadedPos=0,this.fileSize=0,this.nextTimestampInSamples=0,this.reader=new k(e._mainReader)}async readMetadata(){return this.metadataPromise??=(async()=>{for(this.fileSize=await this.input.source.getSize(),this.reader.fileSize=this.fileSize;!this.firstFrameHeader&&this.lastLoadedPos<this.fileSize;)await this.loadNextChunk();if(!this.firstFrameHeader)throw new Error("No MP3 frames found.");this.tracks=[new h(new w(this))]})()}async loadNextChunk(){const e=await this.loadingMutex.acquire();try{s(this.lastLoadedPos<this.fileSize);const t=.5*1024*1024,a=Math.min(this.lastLoadedPos+t,this.fileSize);if(await this.reader.reader.loadRange(this.lastLoadedPos,a),this.lastLoadedPos=a,s(this.lastLoadedPos<=this.fileSize),this.reader.pos===0){const r=this.reader.readId3();r&&(this.reader.pos+=r.size)}this.parseFramesFromLoadedData()}finally{e()}}parseFramesFromLoadedData(){for(;;){const e=this.reader.pos,t=this.reader.readNextFrameHeader();if(!t)break;if(t.startPos+t.totalSize>this.lastLoadedPos){this.reader.pos=e,this.lastLoadedPos=e;break}const a=g(t.mpegVersionId,t.channel);this.reader.pos=t.startPos+a;const r=this.reader.readU32(),i=r===P||r===S;if(this.reader.pos=t.startPos+t.totalSize-1,i)continue;this.firstFrameHeader||(this.firstFrameHeader=t);const n=t.audioSamplesInFrame/t.sampleRate,m={timestamp:this.nextTimestampInSamples/t.sampleRate,duration:n,dataStart:t.startPos,dataSize:t.totalSize};this.loadedSamples.push(m),this.nextTimestampInSamples+=t.audioSamplesInFrame}}async getMimeType(){return"audio/mpeg"}async getTracks(){return await this.readMetadata(),this.tracks}async computeDuration(){await this.readMetadata();const e=this.tracks[0];return s(e),e.computeDuration()}}class w{constructor(e){this.demuxer=e}getId(){return 1}async getFirstTimestamp(){return 0}getTimeResolution(){return s(this.demuxer.firstFrameHeader),this.demuxer.firstFrameHeader.sampleRate/this.demuxer.firstFrameHeader.audioSamplesInFrame}async computeDuration(){const e=await this.getPacket(1/0,{metadataOnly:!0});return(e?.timestamp??0)+(e?.duration??0)}getLanguageCode(){return p}getCodec(){return"mp3"}getNumberOfChannels(){return s(this.demuxer.firstFrameHeader),this.demuxer.firstFrameHeader.channel===3?1:2}getSampleRate(){return s(this.demuxer.firstFrameHeader),this.demuxer.firstFrameHeader.sampleRate}async getDecoderConfig(){return s(this.demuxer.firstFrameHeader),{codec:"mp3",numberOfChannels:this.demuxer.firstFrameHeader.channel===3?1:2,sampleRate:this.demuxer.firstFrameHeader.sampleRate}}getPacketAtIndex(e,t){if(e===-1)return null;const a=this.demuxer.loadedSamples[e];if(!a)return null;let r;return t.metadataOnly?r=x:(this.demuxer.reader.pos=a.dataStart,r=this.demuxer.reader.readBytes(a.dataSize)),new f(r,"key",a.timestamp,a.duration,e,a.dataSize)}async getFirstPacket(e){for(;this.demuxer.loadedSamples.length===0&&this.demuxer.lastLoadedPos<this.demuxer.fileSize;)await this.demuxer.loadNextChunk();return this.getPacketAtIndex(0,e)}async getNextPacket(e,t){const a=u(this.demuxer.loadedSamples,e.timestamp,i=>i.timestamp);if(a===-1)throw new Error("Packet was not created from this track.");const r=a+1;for(;r>=this.demuxer.loadedSamples.length&&this.demuxer.lastLoadedPos<this.demuxer.fileSize;)await this.demuxer.loadNextChunk();return this.getPacketAtIndex(r,t)}async getPacket(e,t){for(;;){const a=c(this.demuxer.loadedSamples,e,r=>r.timestamp);if(a===-1&&this.demuxer.loadedSamples.length>0)return null;if(this.demuxer.lastLoadedPos===this.demuxer.fileSize)return this.getPacketAtIndex(a,t);if(a>=0&&a+1<this.demuxer.loadedSamples.length)return this.getPacketAtIndex(a,t);await this.demuxer.loadNextChunk()}}getKeyPacket(e,t){return this.getPacket(e,t)}getNextKeyPacket(e,t){return this.getNextPacket(e,t)}}
