@jxstjh/jhvideo
Version:
HTML5 jhvideo base on MPEG2-TS Stream Player
180 lines (166 loc) • 6.55 kB
JavaScript
/**
* 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