77 lines
2.0 KiB
JavaScript
77 lines
2.0 KiB
JavaScript
export function buildThumbnailCandidateTimes(durationSeconds) {
|
|
const duration = Number(durationSeconds);
|
|
if (!Number.isFinite(duration) || duration <= 0) {
|
|
return [0.15];
|
|
}
|
|
|
|
const upperBound = Math.max(0.05, duration - 0.05);
|
|
const baseCandidates = [0.15, 0.5, 1, 2, 3.5, 5];
|
|
const candidates = [];
|
|
|
|
for (const candidate of baseCandidates) {
|
|
const clamped = Math.max(0, Math.min(candidate, upperBound));
|
|
if (candidates.length === 0 || Math.abs(candidates[candidates.length - 1] - clamped) > 0.08) {
|
|
candidates.push(Number(clamped.toFixed(3)));
|
|
}
|
|
}
|
|
|
|
if (upperBound > 0.25) {
|
|
const fallback = Number(upperBound.toFixed(3));
|
|
if (Math.abs(candidates[candidates.length - 1] - fallback) > 0.08) {
|
|
candidates.push(fallback);
|
|
}
|
|
}
|
|
|
|
return candidates;
|
|
}
|
|
|
|
export function analyzeFrameLuma(data, options = {}) {
|
|
const bytes = data instanceof Uint8ClampedArray ? data : new Uint8ClampedArray(data ?? []);
|
|
const pixelCount = Math.floor(bytes.length / 4);
|
|
if (pixelCount === 0) {
|
|
return {
|
|
sampleCount: 0,
|
|
averageLuma: 0,
|
|
darkRatio: 1,
|
|
isBlackFrame: true
|
|
};
|
|
}
|
|
|
|
const {
|
|
darknessThreshold = 18,
|
|
maxDarkRatio = 0.985,
|
|
averageThreshold = 22,
|
|
stride = 16
|
|
} = options;
|
|
|
|
let samples = 0;
|
|
let darkSamples = 0;
|
|
let lumaTotal = 0;
|
|
|
|
for (let index = 0; index < pixelCount; index += stride) {
|
|
const offset = index * 4;
|
|
const red = bytes[offset];
|
|
const green = bytes[offset + 1];
|
|
const blue = bytes[offset + 2];
|
|
const alpha = bytes[offset + 3];
|
|
const luma = alpha === 0 ? 0 : 0.2126 * red + 0.7152 * green + 0.0722 * blue;
|
|
lumaTotal += luma;
|
|
samples += 1;
|
|
|
|
if (luma <= darknessThreshold) {
|
|
darkSamples += 1;
|
|
}
|
|
}
|
|
|
|
const averageLuma = samples > 0 ? lumaTotal / samples : 0;
|
|
const darkRatio = samples > 0 ? darkSamples / samples : 1;
|
|
const isBlackFrame = averageLuma <= averageThreshold && darkRatio >= maxDarkRatio;
|
|
|
|
return {
|
|
sampleCount: samples,
|
|
averageLuma,
|
|
darkRatio,
|
|
isBlackFrame
|
|
};
|
|
}
|