Skip to content

RU RU 5 Codecs

J-RAP edited this page Nov 7, 2026 · 3 revisions

5. КОДЕКИ

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.

5.1 ДЕКОДЕРЫ

Статичные изображения

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 CONVERTS

Анимированные изображения

GIF, 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 (см. воспроизведение медиа).

5.2 ЭНКОДЕРЫ

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

Clone this wiki locally