From 00b609f41e2753f542f64fa23a949bc65d64ddd4 Mon Sep 17 00:00:00 2001 From: Reijo Sirila Date: Wed, 24 Jul 2019 10:10:51 +0300 Subject: [PATCH] feature: added support for Q < 128 to RTP-JPEG For Q values less than 128, the luma and chroma quantization tables are computed from the Q value. --- lib/components/jpegdepay/make-qtable.ts | 50 +++++++++++++++++++++++++ lib/components/jpegdepay/parser.ts | 15 ++++++++ lib/utils/clamp.ts | 3 ++ 3 files changed, 68 insertions(+) create mode 100644 lib/components/jpegdepay/make-qtable.ts create mode 100644 lib/utils/clamp.ts diff --git a/lib/components/jpegdepay/make-qtable.ts b/lib/components/jpegdepay/make-qtable.ts new file mode 100644 index 000000000..ad4e65452 --- /dev/null +++ b/lib/components/jpegdepay/make-qtable.ts @@ -0,0 +1,50 @@ +import { clamp } from '../../utils/clamp' +/** + * @function makeQtable + * Creating a quantization table from a Q factor + * Example Code from RFC 2435 Appendix A ported to TypeScript + * + * Default luminance/chrominance quantization tables in RFC example are not in zig-zag order. + * The RFC does mention tables should be in zig-zag ordering, but they say that about the included tables. + * RFC sample code appears to have a mistake. + * All the tested cameras and LGPL projects use zig-zag default tables. + * So we use zig-zaged tables from ISO/IEC 10918-1 Annex K Section K.1 + * @see https://tools.ietf.org/html/rfc2435 + * @see https://www.iso.org/standard/18902.html + */ +// prettier-ignore +const jpegLumaQuantizer = [ + 16, 11, 12, 14, 12, 10, 16, 14, + 13, 14, 18, 17, 16, 19, 24, 40, + 26, 24, 22, 22, 24, 49, 35, 37, + 29, 40, 58, 51, 61, 60, 57, 51, + 56, 55, 64, 72, 92, 78, 64, 68, + 87, 69, 55, 56, 80, 109, 81, 87, + 95, 98, 103, 104, 103, 62, 77, 113, + 121, 112, 100, 120, 92, 101, 103, 99, +] +// prettier-ignore +const jpeChromaQuantizer = [ + 17, 18, 18, 24, 21, 24, 47, 26, + 26, 47, 99, 66, 56, 66, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99 +] + +export function makeQtable(Q: number): Buffer { + const factor = clamp(Q, 1, 99) + const buffer = Buffer.alloc(128) + const S = Q < 50 ? Math.floor(5000 / factor) : 200 - factor * 2 + + for (let i = 0; i < 64; i++) { + const lq = Math.floor((jpegLumaQuantizer[i] * S + 50) / 100) + const cq = Math.floor((jpeChromaQuantizer[i] * S + 50) / 100) + buffer.writeUInt8(clamp(lq, 1, 255), i) + buffer.writeUInt8(clamp(cq, 1, 255), i + 64) + } + return buffer +} diff --git a/lib/components/jpegdepay/parser.ts b/lib/components/jpegdepay/parser.ts index eae26f756..5e6331454 100644 --- a/lib/components/jpegdepay/parser.ts +++ b/lib/components/jpegdepay/parser.ts @@ -6,6 +6,7 @@ import { makeFrameHeader, } from './headers' import { payload } from '../../utils/protocols/rtp' +import { makeQtable } from './make-qtable' /** * Each packet contains a special JPEG header which immediately follows @@ -88,6 +89,20 @@ export function jpegDepayFactory(defaultWidth = 0, defaultHeight = 0) { } fragment = fragment.slice(4 + length) } + // Compute Quantization Table + else if (Q < 128 && fragmentOffset === 0) { + const precision = 0 + const qTable = makeQtable(Q) + metadata = { + typeSpecific, + type, + width, + height, + DRI, + precision, + qTable, + } + } fragments.push(fragment) } diff --git a/lib/utils/clamp.ts b/lib/utils/clamp.ts new file mode 100644 index 000000000..e4ddf081f --- /dev/null +++ b/lib/utils/clamp.ts @@ -0,0 +1,3 @@ +export function clamp(val: number, min: number, max: number): number { + return val > max ? max : val < min ? min : val +}