realview.js
Version:
RealView.js - High-performance video decoder for the browser
107 lines (106 loc) • 3.33 kB
JavaScript
import { parseSPS } from "./SPSParser";
import { parseNALStream } from "./nalstream";
// Merge NAL Units from all packets into a single
// continuous buffer, separated by 4-byte length headers
function mergeNALUs(nalus, length) {
const arr = new Uint8Array(length);
const view = new DataView(arr.buffer);
for (let i = 0, offset = 0; offset < length; i++) {
const unit = nalus[i];
view.setUint32(offset, unit.byteLength);
arr.set(unit, offset + 4);
offset += unit.byteLength + 4;
}
return arr;
}
export class VideoStream {
constructor() {
this.last_packet = null;
this.nalus = [];
this.pps = null;
this.sps = null;
this.samples = [];
this.duration = 0;
this.frame_sum = 0;
this.frame_count = 0;
this.zeroes = 0;
this.byteLength = 0;
}
process(packet) {
if (!this.last_packet) {
this.last_packet = packet;
return;
}
const { nalus } = this;
const next = packet;
packet = this.last_packet;
let size = 0;
let isIDR = false;
for (const nalUnit of parseNALStream(packet.data)) {
switch (nalUnit[0] & 0x1f) {
case 7:
this.sps = nalUnit;
break;
case 8:
this.pps = nalUnit;
break;
case 5:
isIDR = true;
size += nalUnit.length + 4;
nalus.push(nalUnit);
break;
default: // eslint-disable-line no-fallthrough
size += nalUnit.length + 4;
nalus.push(nalUnit);
}
}
const dts_delta = next.dts - packet.dts;
this.samples.push({
size,
isIDR,
offset: this.byteLength,
pts: packet.pts,
dts: packet.dts,
cts: packet.pts - packet.dts,
duration: dts_delta,
});
if (dts_delta) {
this.duration += dts_delta;
this.frame_sum += dts_delta;
this.frame_count++;
}
else {
this.zeroes++;
}
this.byteLength += size;
this.last_packet = next;
}
getTrack() {
const { frame_count, frame_sum, zeroes, samples, sps, pps, nalus, byteLength, } = this;
const frame_rate = Math.round(frame_sum / frame_count);
const duration = this.duration + zeroes * frame_rate;
if (!sps) {
return null;
}
const spsInfo = parseSPS(sps);
const cropping = spsInfo.frame_cropping;
let track = {
type: "video",
pps,
sps,
spsInfo,
width: spsInfo.pic_width_in_mbs * 16 - (cropping.left + cropping.right) * 2,
height: (2 - spsInfo.frame_mbs_only_flag) *
(spsInfo.pic_height_in_map_units * 16) -
(cropping.top + cropping.bottom) * 2,
samples,
duration,
byte_offset: 0,
data: mergeNALUs(nalus, byteLength),
};
this.samples = [];
this.nalus = [];
this.byteLength = 0;
return track;
}
}