From 0230cae85273e272df897088867cc1bede348fd3 Mon Sep 17 00:00:00 2001 From: bivashy Date: Wed, 24 Dec 2025 23:17:21 +0500 Subject: [PATCH] More control of `CaffeineCacheManager`, `PreprocessService` as interface --- .../backend/hls/proxy/config/CacheConfig.java | 21 +++++- .../hls/proxy/config/ProcessConfig.java | 16 +++++ .../controller/ProxyServeController.java | 3 +- .../proxy/service/NoopPreprocessService.java | 13 ++++ .../hls/proxy/service/PreprocessService.java | 66 +----------------- .../RandomEffectPreprocessService.java | 67 +++++++++++++++++++ 6 files changed, 120 insertions(+), 66 deletions(-) create mode 100644 src/main/java/com/backend/hls/proxy/config/ProcessConfig.java create mode 100644 src/main/java/com/backend/hls/proxy/service/NoopPreprocessService.java create mode 100644 src/main/java/com/backend/hls/proxy/service/RandomEffectPreprocessService.java diff --git a/src/main/java/com/backend/hls/proxy/config/CacheConfig.java b/src/main/java/com/backend/hls/proxy/config/CacheConfig.java index 28997ec..5a71470 100644 --- a/src/main/java/com/backend/hls/proxy/config/CacheConfig.java +++ b/src/main/java/com/backend/hls/proxy/config/CacheConfig.java @@ -1,18 +1,37 @@ package com.backend.hls.proxy.config; +import java.util.concurrent.TimeUnit; + import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.caffeine.CaffeineCacheManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import com.backend.hls.proxy.model.SimpleResponse; +import com.github.benmanes.caffeine.cache.Caffeine; + @Configuration @EnableCaching public class CacheConfig { @Bean public CacheManager cacheManager() { - return new CaffeineCacheManager("hlsPlaylistContent", "playlistSegmentContent"); + CaffeineCacheManager cacheManager = new CaffeineCacheManager("hlsPlaylistContent", "playlistSegmentContent"); + cacheManager.setCaffeine(Caffeine.newBuilder() + .expireAfterAccess(1, TimeUnit.HOURS) + .weigher((Object key, Object value) -> { + if (value instanceof byte[] valueBytes) { + return valueBytes.length; + } + if (value instanceof SimpleResponse response && response.getBody() instanceof byte[] body) { + return body.length; + } + return 0; + }) + .maximumWeight(500 * 1024 * 1024) + .recordStats()); + return cacheManager; } } diff --git a/src/main/java/com/backend/hls/proxy/config/ProcessConfig.java b/src/main/java/com/backend/hls/proxy/config/ProcessConfig.java new file mode 100644 index 0000000..0914c7e --- /dev/null +++ b/src/main/java/com/backend/hls/proxy/config/ProcessConfig.java @@ -0,0 +1,16 @@ +package com.backend.hls.proxy.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.backend.hls.proxy.service.NoopPreprocessService; +import com.backend.hls.proxy.service.PreprocessService; + +@Configuration +public class ProcessConfig { + @Bean + public PreprocessService preprocessService() { + return new NoopPreprocessService(); + } + +} diff --git a/src/main/java/com/backend/hls/proxy/controller/ProxyServeController.java b/src/main/java/com/backend/hls/proxy/controller/ProxyServeController.java index 31aba99..b775f4d 100644 --- a/src/main/java/com/backend/hls/proxy/controller/ProxyServeController.java +++ b/src/main/java/com/backend/hls/proxy/controller/ProxyServeController.java @@ -6,6 +6,8 @@ import java.util.Optional; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.cache.annotation.Caching; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -93,7 +95,6 @@ public class ProxyServeController { RangeRequest range = RangeRequest.parse(rangeHeader, contentLength); if (range == null) { - // Invalid range, return 416 Range Not Satisfiable HttpHeaders headers = new HttpHeaders(); headers.add("Content-Range", "bytes */" + contentLength); return new ResponseEntity<>(headers, HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE); diff --git a/src/main/java/com/backend/hls/proxy/service/NoopPreprocessService.java b/src/main/java/com/backend/hls/proxy/service/NoopPreprocessService.java new file mode 100644 index 0000000..d2bc7e1 --- /dev/null +++ b/src/main/java/com/backend/hls/proxy/service/NoopPreprocessService.java @@ -0,0 +1,13 @@ +package com.backend.hls.proxy.service; + +import org.springframework.stereotype.Service; + +@Service +public class NoopPreprocessService implements PreprocessService { + + @Override + public byte[] preprocess(byte[] data) { + return data; + } + +} diff --git a/src/main/java/com/backend/hls/proxy/service/PreprocessService.java b/src/main/java/com/backend/hls/proxy/service/PreprocessService.java index 98ae48c..42cd643 100644 --- a/src/main/java/com/backend/hls/proxy/service/PreprocessService.java +++ b/src/main/java/com/backend/hls/proxy/service/PreprocessService.java @@ -1,67 +1,5 @@ package com.backend.hls.proxy.service; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.Random; -import java.util.stream.Collectors; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Service; - -import com.backend.hls.proxy.util.PipeUtil; - -import net.bramp.ffmpeg.probe.FFmpegProbeResult; - -@Service -public class PreprocessService { - private static final Logger logger = LoggerFactory.getLogger(PreprocessService.class); - - public byte[] preprocess(byte[] data) { - try { - String format = findFormat(data); - logger.info("format is {}", format); - return randomEffectsHLS(data, format, "/usr/bin/ffmpeg"); - } catch (IOException e) { - e.printStackTrace(); - return data; - } - - } - - public static byte[] randomEffectsHLS(byte[] data, String inputFormat, String ffmpegPath) throws IOException { - try (InputStream inputStream = new ByteArrayInputStream(data)) { - String[] effects = { - "hue=s=10", // Color shift - "edgedetect=mode=colormix", // Edge detection - "boxblur=10:1", // Heavy blur - "noise=alls=20:allf=t", // Film grain noise - "colorchannelmixer=.3:.4:.3:0:.3:.4:.3:0:.3:.4:.3", // Vintage - "rotate=0.1*c", // Slight rotation - "scale=iw/2:ih/2" // Pixelate - }; - - Random random = new Random(); - String randomEffect = effects[random.nextInt(effects.length)]; - logger.info("applied effect {}", randomEffect); - - String[] ffmpegArgs = { - "-vf", randomEffect, - "-f", inputFormat, - }; - - return PipeUtil.executeWithPipe(ffmpegPath, inputStream, inputFormat, ffmpegArgs); - } - } - - private String findFormat(byte[] data) throws IOException { - FFmpegProbeResult result = PipeUtil.probeWithPipe("/usr/bin/ffprobe", new ByteArrayInputStream(data)); - logger.info("info: {}", result.streams.stream().map(stream -> stream.codec_type).collect(Collectors.toList())); - if (result.streams.stream().noneMatch(stream -> stream.codec_type.name().equals("VIDEO"))) { - throw new IOException("No video stream found"); - } - return result.format.format_name; - } - +public interface PreprocessService { + byte[] preprocess(byte[] data); } diff --git a/src/main/java/com/backend/hls/proxy/service/RandomEffectPreprocessService.java b/src/main/java/com/backend/hls/proxy/service/RandomEffectPreprocessService.java new file mode 100644 index 0000000..a65f169 --- /dev/null +++ b/src/main/java/com/backend/hls/proxy/service/RandomEffectPreprocessService.java @@ -0,0 +1,67 @@ +package com.backend.hls.proxy.service; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Random; +import java.util.stream.Collectors; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import com.backend.hls.proxy.util.PipeUtil; + +import net.bramp.ffmpeg.probe.FFmpegProbeResult; + +@Service +public class RandomEffectPreprocessService implements PreprocessService { + private static final Logger logger = LoggerFactory.getLogger(PreprocessService.class); + + public byte[] preprocess(byte[] data) { + try { + String format = findFormat(data); + logger.info("format is {}", format); + return randomEffectsHLS(data, format, "/usr/bin/ffmpeg"); + } catch (IOException e) { + e.printStackTrace(); + return data; + } + + } + + public static byte[] randomEffectsHLS(byte[] data, String inputFormat, String ffmpegPath) throws IOException { + try (InputStream inputStream = new ByteArrayInputStream(data)) { + String[] effects = { + "hue=s=10", // Color shift + "edgedetect=mode=colormix", // Edge detection + "boxblur=10:1", // Heavy blur + "noise=alls=20:allf=t", // Film grain noise + "colorchannelmixer=.3:.4:.3:0:.3:.4:.3:0:.3:.4:.3", // Vintage + "rotate=0.1*c", // Slight rotation + "scale=iw/2:ih/2" // Pixelate + }; + + Random random = new Random(); + String randomEffect = effects[random.nextInt(effects.length)]; + logger.info("applied effect {}", randomEffect); + + String[] ffmpegArgs = { + "-vf", randomEffect, + "-f", inputFormat, + }; + + return PipeUtil.executeWithPipe(ffmpegPath, inputStream, inputFormat, ffmpegArgs); + } + } + + private String findFormat(byte[] data) throws IOException { + FFmpegProbeResult result = PipeUtil.probeWithPipe("/usr/bin/ffprobe", new ByteArrayInputStream(data)); + logger.info("info: {}", result.streams.stream().map(stream -> stream.codec_type).collect(Collectors.toList())); + if (result.streams.stream().noneMatch(stream -> stream.codec_type.name().equals("VIDEO"))) { + throw new IOException("No video stream found"); + } + return result.format.format_name; + } + +}