More control of CaffeineCacheManager, PreprocessService as interface
This commit is contained in:
@ -1,18 +1,37 @@
|
|||||||
package com.backend.hls.proxy.config;
|
package com.backend.hls.proxy.config;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.springframework.cache.CacheManager;
|
import org.springframework.cache.CacheManager;
|
||||||
import org.springframework.cache.annotation.EnableCaching;
|
import org.springframework.cache.annotation.EnableCaching;
|
||||||
import org.springframework.cache.caffeine.CaffeineCacheManager;
|
import org.springframework.cache.caffeine.CaffeineCacheManager;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import com.backend.hls.proxy.model.SimpleResponse;
|
||||||
|
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableCaching
|
@EnableCaching
|
||||||
public class CacheConfig {
|
public class CacheConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public CacheManager cacheManager() {
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -6,6 +6,8 @@ import java.util.Optional;
|
|||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
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.HttpHeaders;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
@ -93,7 +95,6 @@ public class ProxyServeController {
|
|||||||
|
|
||||||
RangeRequest range = RangeRequest.parse(rangeHeader, contentLength);
|
RangeRequest range = RangeRequest.parse(rangeHeader, contentLength);
|
||||||
if (range == null) {
|
if (range == null) {
|
||||||
// Invalid range, return 416 Range Not Satisfiable
|
|
||||||
HttpHeaders headers = new HttpHeaders();
|
HttpHeaders headers = new HttpHeaders();
|
||||||
headers.add("Content-Range", "bytes */" + contentLength);
|
headers.add("Content-Range", "bytes */" + contentLength);
|
||||||
return new ResponseEntity<>(headers, HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE);
|
return new ResponseEntity<>(headers, HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE);
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,67 +1,5 @@
|
|||||||
package com.backend.hls.proxy.service;
|
package com.backend.hls.proxy.service;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
public interface PreprocessService {
|
||||||
import java.io.IOException;
|
byte[] preprocess(byte[] data);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user