yuv-rx
Version:
Reading videos as Rx Observables of decoded frames
107 lines (106 loc) • 4.13 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.toRGBAInterleaved = exports.toRGBInterleaved = exports.toRGB = void 0;
const frame_1 = require("./frame");
function clip(n, low, high) {
return n < low ? low : (n > high ? high : n);
}
function clipByte(n) {
return clip(n, 0, 255);
}
/*
Inspired by jcodec implementation
*/
function yuvToRgb(yData, yOffset, uData, uOffset, vData, vOffset, rData, rOffset, gData, gOffset, bData, bOffset) {
const c = yData[yOffset] - 16;
const u = uData[uOffset] - 128;
const v = vData[vOffset] - 128;
const r = (298 * c + 409 * v + 128) >> 8;
const g = (298 * c - 100 * u - 208 * v + 128) >> 8;
const b = (298 * c + 516 * u + 128) >> 8;
rData[rOffset] = clipByte(r);
gData[gOffset] = clipByte(g);
bData[bOffset] = clipByte(b);
}
function yuv420pToRgb(src, dst, conversion) {
let offLuma = 0, offChroma = 0;
const stride = dst.header.width;
for (let i = 0; i < (dst.header.height >> 1); i++) {
for (let k = 0; k < (dst.header.width >> 1); k++) {
const j = k << 1;
conversion(src, dst, offLuma + j, offChroma);
conversion(src, dst, offLuma + j + 1, offChroma);
conversion(src, dst, offLuma + j + stride, offChroma);
conversion(src, dst, offLuma + j + stride + 1, offChroma);
++offChroma;
}
if ((dst.header.width & 0x1) != 0) {
const j = dst.header.width - 1;
conversion(src, dst, offLuma + j, offChroma);
conversion(src, dst, offLuma + j + stride, offChroma);
++offChroma;
}
offLuma += 2 * stride;
}
if ((dst.header.height & 0x1) != 0) {
for (let k = 0; k < (dst.header.width >> 1); k++) {
const j = k << 1;
conversion(src, dst, offLuma + j, offChroma);
conversion(src, dst, offLuma + j + 1, offChroma);
++offChroma;
}
if ((dst.header.width & 0x1) != 0) {
const j = dst.header.width - 1;
conversion(src, dst, offLuma + j, offChroma);
++offChroma;
}
}
}
function toRGB(yuv) {
const w = yuv.header.width;
const h = yuv.header.height;
const rgb = {
header: yuv.header,
colorPlanes: {
r: new frame_1.Gray(w, h),
g: new frame_1.Gray(w, h),
b: new frame_1.Gray(w, h)
}
};
yuv420pToRgb(yuv, rgb, (src, dst, offLuma, offChroma) => {
yuvToRgb(src.colorPlanes.y.data, offLuma, src.colorPlanes.u.data, offChroma, src.colorPlanes.v.data, offChroma, dst.colorPlanes.r.data, offLuma, dst.colorPlanes.g.data, offLuma, dst.colorPlanes.b.data, offLuma);
});
return rgb;
}
exports.toRGB = toRGB;
function toRGBInterleaved(yuv) {
const w = yuv.header.width;
const h = yuv.header.height;
const rgb = {
header: yuv.header,
colorPlanes: {
rgb: new frame_1.InterleavedPlane(w, h, ['r', 'g', 'b']),
}
};
yuv420pToRgb(yuv, rgb, (src, dst, offLuma, offChroma) => {
yuvToRgb(src.colorPlanes.y.data, offLuma, src.colorPlanes.u.data, offChroma, src.colorPlanes.v.data, offChroma, dst.colorPlanes.rgb.data, offLuma * 3, dst.colorPlanes.rgb.data, offLuma * 3 + 1, dst.colorPlanes.rgb.data, offLuma * 3 + 2);
});
return rgb;
}
exports.toRGBInterleaved = toRGBInterleaved;
function toRGBAInterleaved(yuv) {
const w = yuv.header.width;
const h = yuv.header.height;
const rgba = {
header: yuv.header,
colorPlanes: {
rgba: new frame_1.InterleavedPlane(w, h, ['r', 'g', 'b', 'a']),
}
};
yuv420pToRgb(yuv, rgba, (src, dst, offLuma, offChroma) => {
yuvToRgb(src.colorPlanes.y.data, offLuma, src.colorPlanes.u.data, offChroma, src.colorPlanes.v.data, offChroma, dst.colorPlanes.rgba.data, offLuma * 4, dst.colorPlanes.rgba.data, offLuma * 4 + 1, dst.colorPlanes.rgba.data, offLuma * 4 + 2);
dst.colorPlanes.rgba.data[offLuma * 4 + 3] = 0xff;
});
return rgba;
}
exports.toRGBAInterleaved = toRGBAInterleaved;