fix(streaming): include Quadro M2000, etc in special Maxwell cards that can decode HEVC

This commit is contained in:
Christian Benincasa
2025-07-08 21:20:05 -04:00
parent 6438a0db02
commit be92d10bc6
5 changed files with 227 additions and 4 deletions

18
pnpm-lock.yaml generated
View File

@@ -303,6 +303,9 @@ importers:
esbuild-plugin-pino:
specifier: ^2.2.1
version: 2.2.1(esbuild@0.19.12)
fast-check:
specifier: ^4.2.0
version: 4.2.0
fast-glob:
specifier: ^3.3.2
version: 3.3.2
@@ -3807,7 +3810,7 @@ packages:
'@types/sql.js': '*'
'@vercel/postgres': '>=0.8.0'
'@xata.io/client': '*'
better-sqlite3: '>=7'
better-sqlite3: 9.4.5
bun-types: '*'
expo-sqlite: '>=14.0.0'
knex: '*'
@@ -4202,6 +4205,10 @@ packages:
resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==}
engines: {node: '>=4'}
fast-check@4.2.0:
resolution: {integrity: sha512-buxrKEaSseOwFjt6K1REcGMeFOrb0wk3cXifeMAG8yahcE9kV20PjQn1OdzPGL6OBFTbYXfjleNBARf/aCfV1A==}
engines: {node: '>=12.17.0'}
fast-copy@3.0.2:
resolution: {integrity: sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==}
@@ -6027,6 +6034,9 @@ packages:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'}
pure-rand@7.0.1:
resolution: {integrity: sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==}
qs@6.12.0:
resolution: {integrity: sha512-trVZiI6RMOkO476zLGaBIzszOdFPnCCXHPG9kn0yuS1uz6xdVxPfZdB3vUig9pxPFDM9BRAgz/YUIVQ1/vuiUg==}
engines: {node: '>=0.6'}
@@ -11255,6 +11265,10 @@ snapshots:
iconv-lite: 0.4.24
tmp: 0.0.33
fast-check@4.2.0:
dependencies:
pure-rand: 7.0.1
fast-copy@3.0.2: {}
fast-decode-uri-component@1.0.1: {}
@@ -13268,6 +13282,8 @@ snapshots:
punycode@2.3.1: {}
pure-rand@7.0.1: {}
qs@6.12.0:
dependencies:
side-channel: 1.0.6

View File

@@ -96,6 +96,7 @@
"dotenv-cli": "^7.4.1",
"drizzle-kit": "^0.30.4",
"esbuild-plugin-pino": "^2.2.1",
"fast-check": "^4.2.0",
"fast-glob": "^3.3.2",
"globals": "^15.0.0",
"kysely-ctl": "^0.9.0",

View File

@@ -0,0 +1,204 @@
import fc from 'fast-check';
import { VideoFormats } from '../constants.ts';
import {
PixelFormatYuv420P,
PixelFormatYuv420P10Le,
} from '../format/PixelFormat.ts';
import { NvidiaHardwareCapabilities } from './NvidiaHardwareCapabilities.ts';
describe('NvidiaHardwareCapabilities', () => {
test('Quadro M2000 can decode HEVC 10-bit', () => {
const capabilities = new NvidiaHardwareCapabilities('Quadro M2000', 52);
expect(
capabilities.canDecode(
VideoFormats.Hevc,
undefined,
new PixelFormatYuv420P10Le(),
),
).toBeTruthy();
});
test('any card SM 6.0 or higher can decode HEVC', () => {
fc.assert(
fc.property(fc.string(), fc.integer(), (model, arch) => {
const capabilities = new NvidiaHardwareCapabilities(model, arch);
const val = arch >= 60;
expect(
capabilities.canDecode(
VideoFormats.Hevc,
undefined,
new PixelFormatYuv420P10Le(),
),
).toBe(val);
}),
);
});
test('any card SM 6.0 or higher can encode 10-bit HEVC', () => {
fc.assert(
fc.property(fc.string(), fc.integer(), (model, arch) => {
const capabilities = new NvidiaHardwareCapabilities(model, arch);
const val = arch >= 60;
expect(
capabilities.canEncode(
VideoFormats.Hevc,
undefined,
new PixelFormatYuv420P10Le(),
),
).toBe(val);
}),
);
});
test('any card SM 5.2 or higher can encode 8-bit HEVC', () => {
fc.assert(
fc.property(fc.string(), fc.integer(), (model, arch) => {
const capabilities = new NvidiaHardwareCapabilities(model, arch);
const val = arch >= 52;
expect(
capabilities.canEncode(
VideoFormats.Hevc,
undefined,
new PixelFormatYuv420P(),
),
).toBe(val);
}),
);
});
test('any card can decode 8-bit H264', () => {
fc.assert(
fc.property(fc.string(), fc.integer(), (model, arch) => {
const capabilities = new NvidiaHardwareCapabilities(model, arch);
expect(
capabilities.canDecode(
VideoFormats.H264,
undefined,
new PixelFormatYuv420P(),
),
).toBeTruthy();
}),
);
});
test('no card can decode 10-bit H264', () => {
fc.assert(
fc.property(fc.string(), fc.integer(), (model, arch) => {
const capabilities = new NvidiaHardwareCapabilities(model, arch);
expect(
capabilities.canDecode(
VideoFormats.H264,
undefined,
new PixelFormatYuv420P10Le(),
),
).toBeFalsy();
}),
);
});
test('no card can encode 10-bit H264', () => {
fc.assert(
fc.property(fc.string(), fc.integer(), (model, arch) => {
const capabilities = new NvidiaHardwareCapabilities(model, arch);
expect(
capabilities.canEncode(
VideoFormats.H264,
undefined,
new PixelFormatYuv420P10Le(),
),
).toBeFalsy();
}),
);
});
test('any card can decode mpeg2', () => {
fc.assert(
fc.property(fc.string(), fc.integer(), (model, arch) => {
const capabilities = new NvidiaHardwareCapabilities(model, arch);
expect(
capabilities.canDecode(
VideoFormats.Mpeg2Video,
undefined,
new PixelFormatYuv420P(),
),
).toBeTruthy();
}),
);
});
test('any card can decode vc1', () => {
fc.assert(
fc.property(fc.string(), fc.integer(), (model, arch) => {
const capabilities = new NvidiaHardwareCapabilities(model, arch);
expect(
capabilities.canDecode(
VideoFormats.Mpeg2Video,
undefined,
new PixelFormatYuv420P(),
),
).toBeTruthy();
}),
);
});
test('no card can decode mpeg4', () => {
fc.assert(
fc.property(fc.string(), fc.integer(), (model, arch) => {
const capabilities = new NvidiaHardwareCapabilities(model, arch);
expect(
capabilities.canDecode(
VideoFormats.Mpeg4,
undefined,
new PixelFormatYuv420P(),
),
).toBeFalsy();
}),
);
});
test('cards with Compute SM >8.6 can decode AV1', () => {
fc.assert(
fc.property(
fc.string(),
fc.integer().filter((n) => n > 0),
(model, arch) => {
const capabilities = new NvidiaHardwareCapabilities(model, arch);
let ex = false;
if (arch >= 86) {
ex = true;
}
expect(
capabilities.canDecode(
VideoFormats.Av1,
undefined,
new PixelFormatYuv420P(),
),
).toBe(ex);
},
),
);
});
test('cards with Compute SM >8.0 can encode AV1', () => {
fc.assert(
fc.property(
fc.string(),
fc.integer().filter((n) => n > 0),
(model, arch) => {
const capabilities = new NvidiaHardwareCapabilities(model, arch);
let ex = false;
if (arch >= 80) {
ex = true;
}
expect(
capabilities.canEncode(
VideoFormats.Av1,
undefined,
new PixelFormatYuv420P(),
),
).toBe(ex);
},
),
);
});
});

View File

@@ -8,6 +8,9 @@ const MaxwellGm206Models = new Set([
'GTX 950',
'GTX 960',
'GTX 965M',
'Quadro M2000',
'Quadro M2200M',
'Tesla M4',
]);
// https://developer.nvidia.com/video-encode-and-decode-gpu-support-matrix-new
@@ -74,7 +77,6 @@ export class NvidiaHardwareCapabilities extends BaseFfmpegHardwareCapabilities {
break;
}
// TODO: Check binary capabilities
return canUseHardware;
}
@@ -83,7 +85,6 @@ export class NvidiaHardwareCapabilities extends BaseFfmpegHardwareCapabilities {
_videoProfile: Maybe<string>,
pixelFormat: Maybe<PixelFormat>,
): boolean {
// TODO: Clean this up with ffmpeg builder and consts
const bitDepth = pixelFormat?.bitDepth ?? 8;
if (videoFormat === VideoFormats.Hevc) {

View File

@@ -16,7 +16,8 @@ import { KEYS } from '../../../types/inject.ts';
import { Result } from '../../../types/result.ts';
const NvidiaGpuArchPattern = /SM\s+(\d+\.\d+)/;
const NvidiaGpuModelPattern = /(([G|R]TX|Quadro)\s+[0-9a-zA-Z]+[\sTtIi]+)/;
const NvidiaGpuModelPattern =
/(([G|R]TX|Quadro|Tesla)\s+[0-9a-zA-Z]+[\sTtIi]+)/;
type NvidiaGpuDetectionResponse = {
model?: string;