fix(media-proxy): let video work fit small memory budgets (#3392)

This commit is contained in:
Hampus authored and GitHub committed 2026-10-11 14:14:59 +02:00
1 parent b07dcc368d
commit 44bc29b101
6 files changed
+65 -35

No files matched your search

+3 -1
View File
@@ -37,7 +37,9 @@ pub use metadata::{
MetadataOptions, PreparedMetadata, finish_metadata, metadata_json_with_options,
prepare_metadata,
};
pub use native_cost::{AV_NATIVE_COST_BYTES, av_native_cost, image_transform_cost, metadata_cost};
pub use native_cost::{
AV_NATIVE_COST_BYTES, NativeCost, av_native_cost, image_transform_cost, metadata_cost,
};
pub use nsfw_processing::encode_static_image_for_nsfw;
pub use transform::transform_image;
pub use video_thumbnail::{
@@ -10,6 +10,12 @@ use crate::{
};
pub const AV_NATIVE_COST_BYTES: usize = 256 << 20;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum NativeCost {
Bytes(usize),
UpToBudget(usize),
}
const ANIMATED_CANVAS_BUFFERS: usize = 8;
const RGBA_BYTES_PER_PIXEL: usize = 4;
@@ -37,7 +43,7 @@ pub fn image_transform_cost(
input: &[u8],
options: &ImageOptions,
media_limits: &MediaLimits,
) -> Result<usize, MediaError> {
) -> Result<NativeCost, MediaError> {
ensure_vips_init()?;
let sniffed_mime = mime::sniff(input).mime;
let dims = probe_image_dims(media_limits, input)?;
@@ -50,27 +56,29 @@ pub fn image_transform_cost(
} else {
source_pixels * decode_bytes_per_pixel(sniffed_mime) + output_pixels * encode
};
Ok(input.len().saturating_add(pixel_cost))
Ok(NativeCost::Bytes(input.len().saturating_add(pixel_cost)))
}
pub fn av_native_cost(input_len: usize) -> usize {
input_len
.min(constants::MAX_MEDIA_PROXY_BYTES)
.saturating_add(AV_NATIVE_COST_BYTES)
pub fn av_native_cost(input_len: usize) -> NativeCost {
NativeCost::UpToBudget(
input_len
.min(constants::MAX_MEDIA_PROXY_BYTES)
.saturating_add(AV_NATIVE_COST_BYTES),
)
}
pub fn metadata_cost(input: &[u8], media_limits: &MediaLimits) -> Result<usize, MediaError> {
pub fn metadata_cost(input: &[u8], media_limits: &MediaLimits) -> Result<NativeCost, MediaError> {
let sniffed_mime = mime::sniff(input).mime;
match mime::category(sniffed_mime) {
Some(mime::Category::Image) => {
ensure_vips_init()?;
let dims = probe_image_dims(media_limits, input)?;
let source_pixels = dims.width as usize * dims.height as usize;
Ok(input
.len()
.saturating_add(source_pixels * decode_bytes_per_pixel(sniffed_mime)))
Ok(NativeCost::Bytes(input.len().saturating_add(
source_pixels * decode_bytes_per_pixel(sniffed_mime),
)))
}
Some(mime::Category::Video | mime::Category::Audio) => Ok(av_native_cost(input.len())),
_ => Ok(input.len()),
_ => Ok(NativeCost::Bytes(input.len())),
}
}
@@ -3,7 +3,7 @@
use super::super::nsfw_processing::{VideoNSFWFramesRequest, extract_video_frames_for_nsfw};
use super::super::placeholder::{encode_thumbhash, optional_thumbhash};
use super::super::{
AV_NATIVE_COST_BYTES, MediaError, MetadataOptions, NSFW_PREVIEW_MAX_DIMENSION,
AV_NATIVE_COST_BYTES, MediaError, MetadataOptions, NSFW_PREVIEW_MAX_DIMENSION, NativeCost,
extract_video_thumbnail, metadata_cost, metadata_json_with_options, probe_av_metadata,
};
use super::fixtures::{metadata_value, test_media_limits};
@@ -296,7 +296,7 @@ fn metadata_refuses_an_image_past_the_buffer_bound() {
fn native_cost_of_a_video_past_the_buffer_bound_leaves_out_the_mapped_tail() {
let mp4 = mapped_past_the_buffer_bound(&fixture_h264_mp4());
assert_eq!(
constants::MAX_MEDIA_PROXY_BYTES + AV_NATIVE_COST_BYTES,
NativeCost::UpToBudget(constants::MAX_MEDIA_PROXY_BYTES + AV_NATIVE_COST_BYTES),
metadata_cost(&mp4, &test_media_limits()).expect("video cost")
);
}
@@ -3,7 +3,7 @@
use super::super::image_probe::load_image;
use super::super::native_runtime::ensure_vips_init;
use super::super::transform::source_supports_pages;
use super::super::{ImageOptions, MediaError, image_transform_cost};
use super::super::{ImageOptions, MediaError, NativeCost, image_transform_cost};
use super::fixtures::{animated_mode, metadata_value, transform_image};
use crate::{
mime, native,
@@ -188,6 +188,9 @@ fn transform_cost_charges_the_full_source_decode_for_jxl() {
&super::fixtures::test_media_limits(),
)
.unwrap();
let NativeCost::Bytes(cost) = cost else {
panic!("an image is charged exact bytes")
};
assert!(cost >= 4100 * 4100 * 40, "cost {cost}");
}
@@ -205,5 +208,8 @@ fn transform_cost_charges_svg_at_its_output_size() {
&super::fixtures::test_media_limits(),
)
.unwrap();
let NativeCost::Bytes(cost) = cost else {
panic!("an image is charged exact bytes")
};
assert!(cost < 1 << 20, "cost {cost}");
}