webcodecs-flv
Version:
webcodecs decode flv
146 lines (145 loc) • 4.33 kB
JavaScript
import FLVDemuxer from './flv-demuxer.js';
import PCMPlayer from './pcm-player.js';
class player {
constructor({ el }) {
this.pcmPlayer = null;
this.webglPlayer = null;
this.fetchController = null;
this.decorderVideo = null;
this.decorderAudio = null;
this._demuxer = null;
this.canvas = document.getElementsByTagName(el)[0];
this.context = null;
this.isDraw = false;
this.writer = null;
this.writerVideo = null;
}
play(url) {
this._requestStream(url);
this.context = this.canvas.getContext('2d', { alpha: false });
}
stop() {
if (this.fetchController) {
this.fetchController.abort();
}
}
_requestStream(url) {
this.fetchController = new AbortController();
const signal = this.fetchController.signal;
let _this = this;
fetch(url, { signal }).then(async function respond(response) {
const reader = response.body.getReader();
reader.read().then(function processData({ done, value }) {
if (done) {
return;
}
_this._praseFlv(value);
reader.read().then(processData);
});
});
}
async _praseFlv(data) {
if (!this._demuxer) {
this._demuxer = new FLVDemuxer();
this._demuxer.addEventListener('demuxer', (info) => {
if (info.event == 'videoConfig') {
this._videoDecodes(info.data);
}
if (info.event == 'audioConfig') {
this.createAudio();
this._audioDecodes(info.data);
}
if (info.event == 'videoData') {
this._encodedVideoChunk(info.data);
}
if (info.event == 'audioData') {
this._encodedAudioChunk(info.data);
}
});
this._demuxer.parseChunks(data);
} else {
this._demuxer.parseChunks(data);
}
}
_videoDecodes(data) {
let decorderInit = {
output: (frame) => {
// this.context.drawImage(frame, 0, 0);
this.writerVideo.write(frame);
frame.close();
},
error: (e) => {
console.log(e);
},
};
this.decorderVideo = new VideoDecoder(decorderInit);
const config = {
codec: data.codecString, //指定解码器https://zhuanlan.zhihu.com/p/82266810
codecWidth: data.present_size.width,
codecHeight: data.present_size.height,
hardwareAcceleration: 'prefer-hardware', // hardwareAcceleration: 'no-preference',
framerate: 22,
};
this.decorderVideo.configure(config);
}
_encodedVideoChunk(data) {
const chunk = new EncodedVideoChunk({
data: data.data,
timestamp: data.pts,
type: data.isKeyframe ? 'key' : 'delta',
});
this.decorderVideo.decode(chunk);
}
_audioDecodes(data) {
let decorderInit = {
output: async (audioData) => {
await this.writer.write(audioData);
audioData.close();
},
error: (e) => {
console.log(e);
},
};
this.decorderAudio = new AudioDecoder(decorderInit);
const config = {
codec: data.codec,
sampleRate: data.samplingRate,
numberOfChannels: data.channelCount,
};
this.decorderAudio.configure(config);
if (!this.pcmPlayer) {
this.pcmPlayer = new PCMPlayer({
encoding: '16bitInt',
channels: data.channelCount,
sampleRate: data.samplingRate,
flushingTime: 1000,
});
}
}
_encodedAudioChunk(data) {
const chunk = new EncodedAudioChunk({
data: data.data,
timestamp: data.timestamp,
type: 'key',
});
this.decorderAudio.decode(chunk);
}
createAudio() {
const Video = document.createElement('video');
// audio.style.display = 'none';
Video.autoplay = true;
document.body.appendChild(Video);
const generator = new MediaStreamTrackGenerator({
kind: 'audio',
});
const generatorVideo = new MediaStreamTrackGenerator({
kind: 'video',
});
const { writable } = generator;
this.writerVideo = generatorVideo.writable.getWriter();
this.writer = writable.getWriter();
const mediaStream = new MediaStream([generator, generatorVideo]);
Video.srcObject = mediaStream;
}
}
export default player;