Repository navigation
RU RU 5 Codecs
Warning
Экспериментально: decode, reader, sniff и encoder могут измениться в любом обновлении, поэтому пока не
используйте их в своих модах (см. публичные и внутренние пакеты).
Note
Настраивается: codecs.maxImageSourceSize, codecs.png.failOnCorruptedData, codecs.png.useBKGDChunk,
codecs.svg.maxSize и codecs.vp8.brokenTokens. Смотрите их комментарии в config/watermedia.toml.
CodecsAPI выдаёт декодер (ImageReader) и энкодер (Encoder). Изображения используют наши кодеки на чистой Java, без
запуска и без FFmpeg; видео проходит через FFmpeg.
PNG, JPEG, WebP, SVG и Netpbm.
ImageData image = CodecsAPI.decode(bytes); // A byte[], A ByteBuffer OR AN InputStream
ByteBuffer pixels = image.frames()[0]; // TIGHTLY PACKED; JPEG AND WEBP COME ROTATED TO THEIR EXIF ORIENTATION
PixelFormat format = image.format(); // BGRA (GRAY FOR GRAYSCALE JPEGS)
int width = image.width();
int height = image.height();
ImageData rgba = CodecsAPI.decode(bytes, PixelFormat.RGBA); // ASK FOR BGRA, RGBA OR GRAY AND IT CONVERTSGIF, APNG и анимированный WebP. Ридер проходит по кадрам один за другим, а не держит их все в памяти.
try (ImageReader reader = CodecsAPI.reader(buffer)) { // DON'T CHANGE buffer UNTIL THE READER CLOSES
int frames = reader.frameCount();
boolean forever = reader.repeat() == ImageData.REPEAT_FOREVER;
reader.addFilter(new DownscaleFilter(0.5f)); // OPTIONAL: SHRINKS EVERY FRAME, NEVER ENLARGES
while (reader.hasNext()) {
ByteBuffer frame = reader.next(); // REUSED ON THE NEXT CALL: COPY IT TO KEEP IT
long delayMs = reader.delay(); // HOW LONG THIS FRAME STAYS ON SCREEN
}
boolean rewound = reader.reset(); // BACK TO THE FIRST FRAME; false WHEN IT CAN'T
ImageData all = reader.readAll(); // OR EVERY FRAME LEFT AT ONCE, AS COPIES
}MediaType type = CodecsAPI.sniff(buffer); // IMAGE, VIDEO, AUDIO... FROM THE FIRST BYTES
ImageMetadata info = reader.metadata(); // ON AN OPEN READER: title(), authors()... AND value(CodecsAPI.METAKEY_EXIF)
try {
ImageData image = CodecsAPI.decode(bytes);
} catch (UnsupportedFormatException unsupported) {
// A VALID FILE USING A FEATURE THE DECODER LACKS: FALL BACK TO ANOTHER DECODER
} catch (CodecException broken) {
// A BROKEN OR OVERSIZED FILE
}Для некоторых форматов, которые sniff распознаёт как изображения (BMP, TIFF, AVIF...), декодера пока нет. Для ваших
собственных движков new BcReader(buffer) читает сжатые блоки BC1, BC3 и BC7 текстуры DDS; встроенные движки пока их не
рисуют.
Видео и аудио декодируются внутри плееров: создайте плеер через PlayerAPI.player (см.
воспроизведение медиа).
Set<VideoCodec> videoCodecs = Container.MP4.video(); // WHAT EACH CONTAINER ACCEPTS; A TRACK THAT
Set<AudioCodec> audioCodecs = Container.MP4.audio(); // DOESN'T FIT IS REFUSED BEFORE THE FILE IS TOUCHEDКаждый энкодер принимает один кадр за вызов в формате Encoder.Video с постоянно растущими временными метками в
микросекундах; каждый кадр длится до следующего. Передавайте данные каждому энкодеру из одного потока.
PNG (записывается как APNG, если получает больше одного кадра) и GIF, без FFmpeg. Они принимают кадры RGBA, BGRA, RGB или GRAY.
// STATIC: ONE FRAME AND NO AUDIO TRACK
Encoder.Video png = new Encoder.Video(VideoCodec.PNG, 512, 512, PixelFormat.RGBA, 1);
try (Encoder encoder = CodecsAPI.encoder(Container.PNG, Path.of("logo.png"), png, null)) {
encoder.video(pixels, 0);
}
// ANIMATED: MORE FRAMES; THE FULL CONSTRUCTOR ADDS QUALITY (0 TO 100, 50 BY DEFAULT) AND PLAYS (0 LOOPS FOREVER)
Encoder.Video gif = new Encoder.Video(VideoCodec.GIF, 320, 240, PixelFormat.BGRA, 30, 80, 0);
try (Encoder encoder = CodecsAPI.encoder(Container.GIF, Path.of("clip.gif"), gif, null)) {
for (int i = 0; i < frames.length; i++) encoder.video(frames[i], i * 33_333L);
}MP4, MOV, MKV и WebM через FFmpeg, плюс Ogg только для аудио. Они принимают любой несжатый PixelFormat.
Encoder.Video video = new Encoder.Video(VideoCodec.H264, 1280, 720, PixelFormat.BGRA, 60); // THE FULL CONSTRUCTOR SETS QUALITY, LIKE ON IMAGES
Encoder.Audio audio = new Encoder.Audio(AudioCodec.AAC, 48_000, 2, SampleType.S16);
try (Encoder encoder = CodecsAPI.encoder(Container.MP4, Path.of("clip.mp4"), video, audio)) { // IOException WITHOUT FFMPEG
encoder.video(frame, ptsUs); // EXACTLY video.frameBytes() BYTES
encoder.audio(pcm); // INTERLEAVED SAMPLES FROM TIME ZERO: PAD A LATE START WITH SILENCE
} // close() FINISHES THE FILE, WRITTEN AS <file>.part UNTIL THEN