UNPKG

@jxstjh/jhvideo

Version:

HTML5 jhvideo base on MPEG2-TS Stream Player

180 lines (166 loc) 6.55 kB
/** * pcm-processor */ import { alaw, mulaw } from 'alawmulaw' class PCMResampler { tempSamples = [] constructor(options, output, root = null) { this.options = options this.output = output this.root = root // this.root.output({ // type: 'created', // ...this.options // }) } input(inputSamples) { let { fromSampleRate, audioSample, audioBitwidth } = this.options let dataLen = audioSample * 10 * 2 / 1000 let compression = fromSampleRate / audioSample let length = Math.floor(inputSamples.length / compression) let samples = new Float32Array(length) let index = 0 let inputIndex = 0 while (index < length) { samples[index] = inputSamples[inputIndex] inputIndex += compression // 每次都跳过4个数据 index++ } let sampleLen = samples.length let dataView = null if (audioBitwidth === 16) { // 采样精度为16 let buffer = new ArrayBuffer(sampleLen * 2) dataView = new DataView(buffer) let offset = 0 for (let i = 0; i < samples.length; i++, offset += 2) { let s = Math.max(-1, Math.min(1, samples[i])) dataView.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7fff, true) } } else if (audioBitwidth === 8) { // 采样精度为8 let buffer = new ArrayBuffer(sampleLen) dataView = new DataView(buffer) let offset = 0 for (let i = 0; i < samples.length; i++, offset++) { let s = Math.max(-1, Math.min(1, samples[i])) let val = s < 0 ? s * 0x8000 : s * 0x7fff val = parseInt(255 / (65535 / (val + 32768)), 10) dataView.setInt8(offset, val, true) } } if (dataView && dataView.buffer) { let bufferAudioPCM = new Uint8Array(dataView.buffer) let iSampleIndex = this.tempSamples.length for (let i = 0, iBufferLen = bufferAudioPCM.byteLength; i < iBufferLen; i++) { this.tempSamples[iSampleIndex++] = bufferAudioPCM[i] if (this.tempSamples.length === dataLen * 2) { // console.log(this.tempSamples) this.output(new Uint8Array(this.tempSamples.slice(0))) this.tempSamples = [] iSampleIndex = 0 } } } } } class PCMEncoder { constructor(options, output, root = null) { this.options = options this.output = output this.root = root let { audioType, fromSampleRate, audioSample, audioChannel, audioBitrate } = this.options // this.output({ audioType, fromSampleRate, audioSample, audioChannel, audioBitrate }) switch (audioType) { case 'g711a': this.encoder = alaw this.encode = (pcmSamples) => { if (this.options.bFileWrite) { this.output({ type: 'pcm', pcmSamples }) } let alawSamples = alaw.encode(new Int16Array(pcmSamples.buffer)) this.output(alawSamples.buffer) } break case 'g711u': this.encoder = mulaw this.encode = (pcmSamples) => { let mulawSamples = mulaw.encode(new Int16Array(pcmSamples.buffer)) this.output(mulawSamples.buffer) } break case 'aac': this.encoder = new AudioEncoder({ output: (chunk) => { let accSamples = new ArrayBuffer(chunk.byteLength) chunk.copyTo(accSamples) this.output(accSamples) }, error: (err) => { console.error(err) this.encoder = null } }) this.encoder.configure({ codec: 'mp4a.40.2', // aac sampleRate: audioSample, numberOfChannels: audioChannel, bitrate: audioBitrate, }) this.encode = (pcmSamples) => { let audioData = new AudioData({ format: 'f32-planar', sampleRate: fromSampleRate, numberOfChannels: audioChannel, numberOfFrames: pcmSamples.length / audioChannel, timestamp: 0, data: pcmSamples }) this.encoder.encode(audioData) } break default: // mp3 // this.encoder = new lamejs.Mp3Encoder(audioChannel, audioSampleRate, audioBitrate / 1000) // this.encode = (inputBuffer) => { // let pcmSamples = inputBuffer.getChannelData(0) // float32array // let mp3Samples = this.encoder.encodeBuffer(new Int16Array(pcmSamples.buffer)) // console.log('encode func', inputBuffer, pcmSamples, new Int16Array(pcmSamples.buffer), mp3Samples) // this.eventsCenter.emit('stream.output', mp3Samples.buffer) // } break } } /** * * @param {float32array} pcmSamples */ input(pcmSamples) { this.encode(pcmSamples) } } class PCMProcessor { constructor(options, output) { this.options = options this.output = output this.encoder = new PCMEncoder(options, (samples) => { // console.log(samples) this.output(samples) }, this) this.resampler = new PCMResampler(options, (samples) => { // this.output(samples) this.encoder.input(samples) }, this) } /** * * @param {Float32Array} inputSamples */ input(inputSamples) { this.resampler.input(inputSamples) } } export default PCMProcessor