aboutsummaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorXe Iaso <me@xeiaso.net>2024-05-28 10:08:04 -0400
committerXe Iaso <me@xeiaso.net>2024-05-28 10:09:26 -0400
commit479407203e6d6654974a4bd340f576cb0b167f55 (patch)
treed3c78f9ad4fee1ab1e51b735218764be27ce799f /internal
parent79d43f5be33f97b36402ec8de7db51e36b1e7f45 (diff)
downloadx-479407203e6d6654974a4bd340f576cb0b167f55.tar.xz
x-479407203e6d6654974a4bd340f576cb0b167f55.zip
internal/avif: remove this package
Replace this with a bunch of smaller packages that embed WebAssembly libraries for the following functionality: * AVIF encoding * JXL encoding * WEBP encoding Also include HEIC decoding and add JXL encoding support to uploud/xedn Signed-off-by: Xe Iaso <me@xeiaso.net>
Diffstat (limited to 'internal')
-rw-r--r--internal/avif/COPYING121
-rw-r--r--internal/avif/README.md117
-rw-r--r--internal/avif/av1.c215
-rw-r--r--internal/avif/av1.h44
-rw-r--r--internal/avif/avif.go201
-rw-r--r--internal/avif/example_test.go42
-rw-r--r--internal/avif/hacker-nest.avifbin22522 -> 0 bytes
-rw-r--r--internal/avif/mp4.go799
8 files changed, 0 insertions, 1539 deletions
diff --git a/internal/avif/COPYING b/internal/avif/COPYING
deleted file mode 100644
index 0e259d4..0000000
--- a/internal/avif/COPYING
+++ /dev/null
@@ -1,121 +0,0 @@
-Creative Commons Legal Code
-
-CC0 1.0 Universal
-
- CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
- LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
- ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
- INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
- REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
- PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
- THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
- HEREUNDER.
-
-Statement of Purpose
-
-The laws of most jurisdictions throughout the world automatically confer
-exclusive Copyright and Related Rights (defined below) upon the creator
-and subsequent owner(s) (each and all, an "owner") of an original work of
-authorship and/or a database (each, a "Work").
-
-Certain owners wish to permanently relinquish those rights to a Work for
-the purpose of contributing to a commons of creative, cultural and
-scientific works ("Commons") that the public can reliably and without fear
-of later claims of infringement build upon, modify, incorporate in other
-works, reuse and redistribute as freely as possible in any form whatsoever
-and for any purposes, including without limitation commercial purposes.
-These owners may contribute to the Commons to promote the ideal of a free
-culture and the further production of creative, cultural and scientific
-works, or to gain reputation or greater distribution for their Work in
-part through the use and efforts of others.
-
-For these and/or other purposes and motivations, and without any
-expectation of additional consideration or compensation, the person
-associating CC0 with a Work (the "Affirmer"), to the extent that he or she
-is an owner of Copyright and Related Rights in the Work, voluntarily
-elects to apply CC0 to the Work and publicly distribute the Work under its
-terms, with knowledge of his or her Copyright and Related Rights in the
-Work and the meaning and intended legal effect of CC0 on those rights.
-
-1. Copyright and Related Rights. A Work made available under CC0 may be
-protected by copyright and related or neighboring rights ("Copyright and
-Related Rights"). Copyright and Related Rights include, but are not
-limited to, the following:
-
- i. the right to reproduce, adapt, distribute, perform, display,
- communicate, and translate a Work;
- ii. moral rights retained by the original author(s) and/or performer(s);
-iii. publicity and privacy rights pertaining to a person's image or
- likeness depicted in a Work;
- iv. rights protecting against unfair competition in regards to a Work,
- subject to the limitations in paragraph 4(a), below;
- v. rights protecting the extraction, dissemination, use and reuse of data
- in a Work;
- vi. database rights (such as those arising under Directive 96/9/EC of the
- European Parliament and of the Council of 11 March 1996 on the legal
- protection of databases, and under any national implementation
- thereof, including any amended or successor version of such
- directive); and
-vii. other similar, equivalent or corresponding rights throughout the
- world based on applicable law or treaty, and any national
- implementations thereof.
-
-2. Waiver. To the greatest extent permitted by, but not in contravention
-of, applicable law, Affirmer hereby overtly, fully, permanently,
-irrevocably and unconditionally waives, abandons, and surrenders all of
-Affirmer's Copyright and Related Rights and associated claims and causes
-of action, whether now known or unknown (including existing as well as
-future claims and causes of action), in the Work (i) in all territories
-worldwide, (ii) for the maximum duration provided by applicable law or
-treaty (including future time extensions), (iii) in any current or future
-medium and for any number of copies, and (iv) for any purpose whatsoever,
-including without limitation commercial, advertising or promotional
-purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
-member of the public at large and to the detriment of Affirmer's heirs and
-successors, fully intending that such Waiver shall not be subject to
-revocation, rescission, cancellation, termination, or any other legal or
-equitable action to disrupt the quiet enjoyment of the Work by the public
-as contemplated by Affirmer's express Statement of Purpose.
-
-3. Public License Fallback. Should any part of the Waiver for any reason
-be judged legally invalid or ineffective under applicable law, then the
-Waiver shall be preserved to the maximum extent permitted taking into
-account Affirmer's express Statement of Purpose. In addition, to the
-extent the Waiver is so judged Affirmer hereby grants to each affected
-person a royalty-free, non transferable, non sublicensable, non exclusive,
-irrevocable and unconditional license to exercise Affirmer's Copyright and
-Related Rights in the Work (i) in all territories worldwide, (ii) for the
-maximum duration provided by applicable law or treaty (including future
-time extensions), (iii) in any current or future medium and for any number
-of copies, and (iv) for any purpose whatsoever, including without
-limitation commercial, advertising or promotional purposes (the
-"License"). The License shall be deemed effective as of the date CC0 was
-applied by Affirmer to the Work. Should any part of the License for any
-reason be judged legally invalid or ineffective under applicable law, such
-partial invalidity or ineffectiveness shall not invalidate the remainder
-of the License, and in such case Affirmer hereby affirms that he or she
-will not (i) exercise any of his or her remaining Copyright and Related
-Rights in the Work or (ii) assert any associated claims and causes of
-action with respect to the Work, in either case contrary to Affirmer's
-express Statement of Purpose.
-
-4. Limitations and Disclaimers.
-
- a. No trademark or patent rights held by Affirmer are waived, abandoned,
- surrendered, licensed or otherwise affected by this document.
- b. Affirmer offers the Work as-is and makes no representations or
- warranties of any kind concerning the Work, express, implied,
- statutory or otherwise, including without limitation warranties of
- title, merchantability, fitness for a particular purpose, non
- infringement, or the absence of latent or other defects, accuracy, or
- the present or absence of errors, whether or not discoverable, all to
- the greatest extent permissible under applicable law.
- c. Affirmer disclaims responsibility for clearing rights of other persons
- that may apply to the Work or any use thereof, including without
- limitation any person's Copyright and Related Rights in the Work.
- Further, Affirmer disclaims responsibility for obtaining any necessary
- consents, permissions or other rights required for any use of the
- Work.
- d. Affirmer understands and acknowledges that Creative Commons is not a
- party to this document and has no duty or obligation with respect to
- this CC0 or use of the Work.
diff --git a/internal/avif/README.md b/internal/avif/README.md
deleted file mode 100644
index 9c9fef6..0000000
--- a/internal/avif/README.md
+++ /dev/null
@@ -1,117 +0,0 @@
-# go-avif [![Build Status](https://travis-ci.org/Kagami/go-avif.svg?branch=master)](https://travis-ci.org/Kagami/go-avif) [![GoDoc](https://godoc.org/github.com/Kagami/go-avif?status.svg)](https://godoc.org/github.com/Kagami/go-avif)
-
-go-avif implements
-AVIF ([AV1 Still Image File Format](https://aomediacodec.github.io/av1-avif/))
-encoder for Go using libaom, the [high quality](https://github.com/Kagami/av1-bench)
-AV1 codec.
-
-## Requirements
-
-Make sure libaom is installed. On typical Linux distro just run:
-
-#### Debian (and derivatives):
-```bash
-sudo apt-get install libaom-dev
-```
-
-#### RHEL (and derivatives):
-```bash
-sudo dnf install libaom-devel
-```
-
-## Usage
-
-To use go-avif in your Go code:
-
-```go
-import "github.com/Kagami/go-avif"
-```
-
-To install go-avif in your $GOPATH:
-
-```bash
-go get github.com/Kagami/go-avif
-```
-
-For further details see [GoDoc documentation](https://godoc.org/github.com/Kagami/go-avif).
-
-## Example
-
-```go
-package main
-
-import (
- "image"
- _ "image/jpeg"
- "log"
- "os"
-
- "github.com/Kagami/go-avif"
-)
-
-func main() {
- if len(os.Args) != 3 {
- log.Fatalf("Usage: %s src.jpg dst.avif", os.Args[0])
- }
-
- srcPath := os.Args[1]
- src, err := os.Open(srcPath)
- if err != nil {
- log.Fatalf("Can't open sorce file: %v", err)
- }
-
- dstPath := os.Args[2]
- dst, err := os.Create(dstPath)
- if err != nil {
- log.Fatalf("Can't create destination file: %v", err)
- }
-
- img, _, err := image.Decode(src)
- if err != nil {
- log.Fatalf("Can't decode source file: %v", err)
- }
-
- err = avif.Encode(dst, img, nil)
- if err != nil {
- log.Fatalf("Can't encode source image: %v", err)
- }
-
- log.Printf("Encoded AVIF at %s", dstPath)
-}
-```
-
-## CLI
-
-go-avif comes with handy CLI utility `avif`. It supports encoding of JPEG and
-PNG files to AVIF:
-
-```bash
-# Compile and put avif binary to $GOPATH/bin
-go get github.com/Kagami/go-avif/...
-
-# Encode JPEG to AVIF with default settings
-avif -e cat.jpg -o kitty.avif
-
-# Encode PNG with slowest speed
-avif -e dog.png -o doggy.avif --best -q 15
-
-# Lossless encoding
-avif -e pig.png -o piggy.avif --lossless
-
-# Show help
-avif -h
-```
-
-Static 64-bit builds for Windows, macOS and Linux are available at
-[releases page](https://github.com/Kagami/go-avif/releases). They include
-latest libaom from git at the moment of build.
-
-## Display
-
-To display resulting AVIF files take a look at software listed
-[here](https://github.com/AOMediaCodec/av1-avif/wiki#demuxers--players). E.g.
-use [avif.js](https://kagami.github.io/avif.js/) web viewer.
-
-## License
-
-go-avif is licensed under [CC0](COPYING).
diff --git a/internal/avif/av1.c b/internal/avif/av1.c
deleted file mode 100644
index 24cbeb8..0000000
--- a/internal/avif/av1.c
+++ /dev/null
@@ -1,215 +0,0 @@
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-#include <aom/aom_encoder.h>
-#include <aom/aomcx.h>
-#include "av1.h"
-
-#define SET_CODEC_CONTROL(ctrl, val) \
- {if (aom_codec_control(ctx, ctrl, val)) return AVIF_ERROR_CODEC_INIT;}
-
-typedef struct {
- aom_img_fmt_t fmt;
- int dst_c_dec_h;
- int dst_c_dec_v;
- int bps;
- int bytes_per_sample;
-} avif_format;
-
-static avif_format convert_subsampling(const avif_subsampling subsampling) {
- avif_format fmt = { 0 };
- switch (subsampling) {
- case AVIF_SUBSAMPLING_I420:
- fmt.fmt = AOM_IMG_FMT_I420;
- fmt.dst_c_dec_h = 2;
- fmt.dst_c_dec_v = 2;
- fmt.bps = 12;
- fmt.bytes_per_sample = 1;
- break;
- default:
- assert(0);
- }
- return fmt;
-}
-
-// We don't use aom_img_wrap() because it forces padding for odd picture
-// sizes (c) libaom/common/y4minput.c
-static void convert_frame(const avif_frame *frame, aom_image_t *aom_frame) {
- memset(aom_frame, 0, sizeof(*aom_frame));
- avif_format fmt = convert_subsampling(frame->subsampling);
- aom_frame->fmt = fmt.fmt;
- aom_frame->w = aom_frame->d_w = frame->width;
- aom_frame->h = aom_frame->d_h = frame->height;
- aom_frame->x_chroma_shift = fmt.dst_c_dec_h >> 1;
- aom_frame->y_chroma_shift = fmt.dst_c_dec_v >> 1;
- aom_frame->bps = fmt.bps;
- int pic_sz = frame->width * frame->height * fmt.bytes_per_sample;
- int c_w = (frame->width + fmt.dst_c_dec_h - 1) / fmt.dst_c_dec_h;
- c_w *= fmt.bytes_per_sample;
- int c_h = (frame->height + fmt.dst_c_dec_v - 1) / fmt.dst_c_dec_v;
- int c_sz = c_w * c_h;
- aom_frame->stride[AOM_PLANE_Y] = frame->width * fmt.bytes_per_sample;
- aom_frame->stride[AOM_PLANE_U] = aom_frame->stride[AOM_PLANE_V] = c_w;
- aom_frame->planes[AOM_PLANE_Y] = frame->data;
- aom_frame->planes[AOM_PLANE_U] = frame->data + pic_sz;
- aom_frame->planes[AOM_PLANE_V] = frame->data + pic_sz + c_sz;
-}
-
-static int get_frame_stats(aom_codec_ctx_t *ctx,
- const aom_image_t *frame,
- aom_fixed_buf_t *stats) {
- if (aom_codec_encode(ctx, frame, 1/*pts*/, 1/*duration*/, 0/*flags*/))
- return AVIF_ERROR_FRAME_ENCODE;
-
- const aom_codec_cx_pkt_t *pkt = NULL;
- aom_codec_iter_t iter = NULL;
- int got_pkts = 0;
- while ((pkt = aom_codec_get_cx_data(ctx, &iter)) != NULL) {
- got_pkts = 1;
- if (pkt->kind == AOM_CODEC_STATS_PKT) {
- const uint8_t *const pkt_buf = pkt->data.twopass_stats.buf;
- const size_t pkt_size = pkt->data.twopass_stats.sz;
- stats->buf = realloc(stats->buf, stats->sz + pkt_size);
- memcpy((uint8_t *)stats->buf + stats->sz, pkt_buf, pkt_size);
- stats->sz += pkt_size;
- }
- }
- return got_pkts;
-}
-
-static int encode_frame(aom_codec_ctx_t *ctx,
- const aom_image_t *frame,
- avif_buffer *obu) {
- if (aom_codec_encode(ctx, frame, 1/*pts*/, 1/*duration*/, 0/*flags*/))
- return AVIF_ERROR_FRAME_ENCODE;
-
- const aom_codec_cx_pkt_t *pkt = NULL;
- aom_codec_iter_t iter = NULL;
- int got_pkts = 0;
- while ((pkt = aom_codec_get_cx_data(ctx, &iter)) != NULL) {
- got_pkts = 1;
- if (pkt->kind == AOM_CODEC_CX_FRAME_PKT) {
- const uint8_t *const pkt_buf = pkt->data.frame.buf;
- const size_t pkt_size = pkt->data.frame.sz;
- obu->buf = realloc(obu->buf, obu->sz + pkt_size);
- memcpy((uint8_t *)obu->buf + obu->sz, pkt_buf, pkt_size);
- obu->sz += pkt_size;
- }
- }
- return got_pkts;
-}
-
-static avif_error init_codec(aom_codec_iface_t *iface,
- aom_codec_ctx_t *ctx,
- const aom_codec_enc_cfg_t *aom_cfg,
- const avif_config *cfg) {
- if (aom_codec_enc_init(ctx, iface, aom_cfg, 0))
- return AVIF_ERROR_CODEC_INIT;
-
- SET_CODEC_CONTROL(AOME_SET_CPUUSED, cfg->speed)
- SET_CODEC_CONTROL(AOME_SET_CQ_LEVEL, cfg->quality)
- if (cfg->quality == 0) {
- SET_CODEC_CONTROL(AV1E_SET_LOSSLESS, 1)
- }
- SET_CODEC_CONTROL(AV1E_SET_FRAME_PARALLEL_DECODING, 0)
- SET_CODEC_CONTROL(AV1E_SET_TILE_COLUMNS, 1)
- SET_CODEC_CONTROL(AV1E_SET_TILE_ROWS, 1)
-#ifdef AOM_CTRL_AV1E_SET_ROW_MT
- SET_CODEC_CONTROL(AV1E_SET_ROW_MT, 1)
-#endif
-
- return AVIF_OK;
-}
-
-static avif_error do_pass1(aom_codec_ctx_t *ctx,
- const aom_image_t *frame,
- aom_fixed_buf_t *stats) {
- avif_error res = AVIF_OK;
-
- // Calculate frame statistics.
- if ((res = get_frame_stats(ctx, frame, stats)) < 0)
- goto fail;
-
- // Flush encoder.
- while ((res = get_frame_stats(ctx, NULL, stats)) > 0)
- continue;
-
-fail:
- return res < 0 ? res : AVIF_OK;
-}
-
-static avif_error do_pass2(aom_codec_ctx_t *ctx,
- const aom_image_t *frame,
- avif_buffer *obu) {
- avif_error res = AVIF_OK;
-
- // Encode frame.
- if ((res = encode_frame(ctx, frame, obu)) < 0)
- goto fail;
-
- // Flush encoder.
- while ((res = encode_frame(ctx, NULL, obu)) > 0)
- continue;
-
-fail:
- return res < 0 ? res : AVIF_OK;
-}
-
-avif_error avif_encode_frame(const avif_config *cfg,
- const avif_frame *frame,
- avif_buffer *obu) {
- // Validation.
- assert(cfg->threads >= 1);
- assert(cfg->speed >= AVIF_MIN_SPEED && cfg->speed <= AVIF_MAX_SPEED);
- assert(cfg->quality >= AVIF_MIN_QUALITY && cfg->quality <= AVIF_MAX_QUALITY);
- assert(frame->width && frame->height);
-
- // Prepare image.
- aom_image_t aom_frame;
- convert_frame(frame, &aom_frame);
-
- // Setup codec.
- avif_error res = AVIF_OK;
- aom_codec_ctx_t codec;
- aom_fixed_buf_t stats = { NULL, 0 };
- aom_codec_iface_t *iface = aom_codec_av1_cx();
- aom_codec_enc_cfg_t aom_cfg;
- if (aom_codec_enc_config_default(iface, &aom_cfg, 0)) {
- res = AVIF_ERROR_CODEC_INIT;
- goto fail;
- }
- aom_cfg.g_limit = 1;
- aom_cfg.g_w = frame->width;
- aom_cfg.g_h = frame->height;
- aom_cfg.g_timebase.num = 1;
- aom_cfg.g_timebase.den = 24;
- aom_cfg.rc_end_usage = AOM_Q;
- aom_cfg.g_threads = cfg->threads;
-
- // Pass 1.
- aom_cfg.g_pass = AOM_RC_FIRST_PASS;
- if ((res = init_codec(iface, &codec, &aom_cfg, cfg)))
- goto fail;
- if ((res = do_pass1(&codec, &aom_frame, &stats)))
- goto fail;
- if (aom_codec_destroy(&codec)) {
- res = AVIF_ERROR_CODEC_DESTROY;
- goto fail;
- }
-
- // Pass 2.
- aom_cfg.g_pass = AOM_RC_LAST_PASS;
- aom_cfg.rc_twopass_stats_in = stats;
- if ((res = init_codec(iface, &codec, &aom_cfg, cfg)))
- goto fail;
- if ((res = do_pass2(&codec, &aom_frame, obu)))
- goto fail;
- if (aom_codec_destroy(&codec)) {
- res = AVIF_ERROR_CODEC_DESTROY;
- goto fail;
- }
-
-fail:
- free(stats.buf);
- return res;
-}
diff --git a/internal/avif/av1.h b/internal/avif/av1.h
deleted file mode 100644
index d92e198..0000000
--- a/internal/avif/av1.h
+++ /dev/null
@@ -1,44 +0,0 @@
-#pragma once
-
-#include <stdint.h>
-
-enum {
- AVIF_MIN_SPEED = 0,
- AVIF_MAX_SPEED = 8,
- AVIF_MIN_QUALITY = 0,
- AVIF_MAX_QUALITY = 63,
-};
-
-typedef enum {
- AVIF_OK = 0,
- AVIF_ERROR_GENERAL = -1000,
- AVIF_ERROR_CODEC_INIT,
- AVIF_ERROR_CODEC_DESTROY,
- AVIF_ERROR_FRAME_ENCODE,
-} avif_error;
-
-typedef enum {
- AVIF_SUBSAMPLING_I420,
-} avif_subsampling;
-
-typedef struct {
- int threads;
- int speed;
- int quality;
-} avif_config;
-
-typedef struct {
- uint16_t width;
- uint16_t height;
- avif_subsampling subsampling;
- uint8_t *data;
-} avif_frame;
-
-typedef struct {
- void *buf;
- size_t sz;
-} avif_buffer;
-
-avif_error avif_encode_frame(const avif_config *cfg,
- const avif_frame *frame,
- avif_buffer *obu);
diff --git a/internal/avif/avif.go b/internal/avif/avif.go
deleted file mode 100644
index 5975685..0000000
--- a/internal/avif/avif.go
+++ /dev/null
@@ -1,201 +0,0 @@
-// Package avif implements a AVIF image encoder.
-//
-// The AVIF specification is at https://aomediacodec.github.io/av1-avif/.
-package avif
-
-// #cgo CFLAGS: -Wall -O2 -DNDEBUG
-// #cgo pkg-config: aom
-// #include <stdlib.h>
-// #include "av1.h"
-import "C"
-import (
- "fmt"
- "image"
- "io"
- "runtime"
-)
-
-// Encoder constants.
-const (
- MinThreads = 1
- MaxThreads = 64
- MinSpeed = 0
- MaxSpeed = 8
- MinQuality = 0
- MaxQuality = 63
-)
-
-// Options are the encoding parameters. Threads ranges from MinThreads
-// to MaxThreads, 0 means use all available cores. Speed ranges from
-// MinSpeed to MaxSpeed. Quality ranges from MinQuality to MaxQuality,
-// lower is better, 0 means lossless encoding. SubsampleRatio specifies
-// subsampling of the encoded image, nil means 4:2:0.
-type Options struct {
- Threads int
- Speed int
- Quality int
- SubsampleRatio *image.YCbCrSubsampleRatio
-}
-
-// DefaultOptions defines default encoder config.
-var DefaultOptions = Options{
- Threads: 0,
- Speed: 4,
- Quality: 25,
- SubsampleRatio: nil,
-}
-
-// An OptionsError reports that the passed options are not valid.
-type OptionsError string
-
-func (e OptionsError) Error() string {
- return fmt.Sprintf("options error: %s", string(e))
-}
-
-// An EncoderError reports that the encoder error has occured.
-type EncoderError int
-
-func (e EncoderError) ToString() string {
- switch e {
- case C.AVIF_ERROR_GENERAL:
- return "general error"
- case C.AVIF_ERROR_CODEC_INIT:
- return "codec init error"
- case C.AVIF_ERROR_CODEC_DESTROY:
- return "codec destroy error"
- case C.AVIF_ERROR_FRAME_ENCODE:
- return "frame encode error"
- default:
- return "unknown error"
- }
-}
-
-func (e EncoderError) Error() string {
- return fmt.Sprintf("encoder error: %s", e.ToString())
-}
-
-// A MuxerError reports that the muxer error has occured.
-type MuxerError string
-
-func (e MuxerError) Error() string {
- return fmt.Sprintf("muxer error: %s", string(e))
-}
-
-// RGB to BT.709 YCbCr limited range.
-// https://web.archive.org/web/20180421030430/http://www.equasys.de/colorconversion.html
-// TODO(Kagami): Use fixed point, don't calc chroma values for skipped pixels.
-func rgb2yuv(r16, g16, b16 uint32) (uint8, uint8, uint8) {
- r, g, b := float32(r16)/256, float32(g16)/256, float32(b16)/256
- y := 0.183*r + 0.614*g + 0.062*b + 16
- cb := -0.101*r - 0.339*g + 0.439*b + 128
- cr := 0.439*r - 0.399*g - 0.040*b + 128
- return uint8(y), uint8(cb), uint8(cr)
-}
-
-// Encode writes the Image m to w in AVIF format with the given options.
-// Default parameters are used if a nil *Options is passed.
-//
-// NOTE: Image pixels are converted to RGBA first using standard Go
-// library. This is no-op for PNG images and does the right thing for
-// JPEG since they are normally stored as BT.601 full range with some
-// chroma subsampling. Then pixels are converted to BT.709 limited range
-// with specified chroma subsampling.
-//
-// Alpha channel and monochrome are not supported at the moment. Only
-// 4:2:0 8-bit images are supported at the moment.
-func Encode(w io.Writer, m image.Image, o *Options) error {
- // TODO(Kagami): More subsamplings, 10/12 bitdepth, monochrome, alpha.
- // TODO(Kagami): Allow to pass BT.709 YCbCr without extra conversions.
- if o == nil {
- o2 := DefaultOptions
- o = &o2
- } else {
- o2 := *o
- o = &o2
- }
- if o.Threads == 0 {
- o.Threads = runtime.NumCPU()
- if o.Threads > MaxThreads {
- o.Threads = MaxThreads
- }
- }
- if o.SubsampleRatio == nil {
- s := image.YCbCrSubsampleRatio420
- o.SubsampleRatio = &s
- // if yuvImg, ok := m.(*image.YCbCr); ok {
- // o.SubsampleRatio = &yuvImg.SubsampleRatio
- // }
- }
- if o.Threads < MinThreads || o.Threads > MaxThreads {
- return OptionsError("bad threads number")
- }
- if o.Speed < MinSpeed || o.Speed > MaxSpeed {
- return OptionsError("bad speed value")
- }
- if o.Quality < MinQuality || o.Quality > MaxQuality {
- return OptionsError("bad quality value")
- }
- if *o.SubsampleRatio != image.YCbCrSubsampleRatio420 {
- return OptionsError("unsupported subsampling")
- }
- if m.Bounds().Empty() {
- return OptionsError("empty image")
- }
-
- rec := m.Bounds()
- width := rec.Max.X - rec.Min.X
- height := rec.Max.Y - rec.Min.Y
- ySize := width * height
- uSize := ((width + 1) / 2) * ((height + 1) / 2)
- dataSize := ySize + uSize*2
- // Can't pass normal slice inside a struct, see
- // https://github.com/golang/go/issues/14210
- dataPtr := C.malloc(C.size_t(dataSize))
- defer C.free(dataPtr)
- data := (*[1 << 30]byte)(dataPtr)[:dataSize:dataSize]
-
- yPos := 0
- uPos := ySize
- for j := rec.Min.Y; j < rec.Max.Y; j++ {
- for i := rec.Min.X; i < rec.Max.X; i++ {
- r16, g16, b16, _ := m.At(i, j).RGBA()
- y, u, v := rgb2yuv(r16, g16, b16)
- data[yPos] = y
- yPos++
- // TODO(Kagami): Resample chroma planes with some better filter.
- if (i-rec.Min.X)&1 == 0 && (j-rec.Min.Y)&1 == 0 {
- data[uPos] = u
- data[uPos+uSize] = v
- uPos++
- }
- }
- }
-
- cfg := C.avif_config{
- threads: C.int(o.Threads),
- speed: C.int(o.Speed),
- quality: C.int(o.Quality),
- }
- frame := C.avif_frame{
- width: C.uint16_t(width),
- height: C.uint16_t(height),
- subsampling: C.AVIF_SUBSAMPLING_I420,
- data: (*C.uint8_t)(dataPtr),
- }
- obu := C.avif_buffer{
- buf: nil,
- sz: 0,
- }
- defer C.free(obu.buf)
- // TODO(Kagami): Error description.
- if eErr := C.avif_encode_frame(&cfg, &frame, &obu); eErr != 0 {
- return EncoderError(eErr)
- }
-
- obuData := (*[1 << 30]byte)(obu.buf)[:obu.sz:obu.sz]
- if mErr := muxFrame(w, m, *o.SubsampleRatio, obuData); mErr != nil {
- return MuxerError(mErr.Error())
- }
-
- return nil
-}
diff --git a/internal/avif/example_test.go b/internal/avif/example_test.go
deleted file mode 100644
index 235cbde..0000000
--- a/internal/avif/example_test.go
+++ /dev/null
@@ -1,42 +0,0 @@
-package avif_test
-
-import (
- "image"
- _ "image/jpeg"
- "log"
- "os"
-
- "within.website/x/internal/avif"
-)
-
-const usageHelp = "Usage: %s src.jpg dst.avif"
-
-func Example() {
- if len(os.Args) != 3 {
- log.Fatalf(usageHelp, os.Args[0])
- }
-
- srcPath := os.Args[1]
- src, err := os.Open(srcPath)
- if err != nil {
- log.Fatalf("Can't open sorce file: %v", err)
- }
-
- dstPath := os.Args[2]
- dst, err := os.Create(dstPath)
- if err != nil {
- log.Fatalf("Can't create destination file: %v", err)
- }
-
- img, _, err := image.Decode(src)
- if err != nil {
- log.Fatalf("Can't decode source file: %v", err)
- }
-
- err = avif.Encode(dst, img, nil)
- if err != nil {
- log.Fatalf("Can't encode source image: %v", err)
- }
-
- log.Printf("Encoded AVIF at %s", dstPath)
-}
diff --git a/internal/avif/hacker-nest.avif b/internal/avif/hacker-nest.avif
deleted file mode 100644
index b927f01..0000000
--- a/internal/avif/hacker-nest.avif
+++ /dev/null
Binary files differ
diff --git a/internal/avif/mp4.go b/internal/avif/mp4.go
deleted file mode 100644
index bfed208..0000000
--- a/internal/avif/mp4.go
+++ /dev/null
@@ -1,799 +0,0 @@
-package avif
-
-import (
- "encoding/binary"
- "image"
- "io"
-)
-
-type fourCC [4]byte
-
-var (
- boxTypeFTYP = fourCC{'f', 't', 'y', 'p'}
- boxTypeMDAT = fourCC{'m', 'd', 'a', 't'}
- boxTypeMETA = fourCC{'m', 'e', 't', 'a'}
- boxTypeHDLR = fourCC{'h', 'd', 'l', 'r'}
- boxTypePITM = fourCC{'p', 'i', 't', 'm'}
- boxTypeILOC = fourCC{'i', 'l', 'o', 'c'}
- boxTypeIINF = fourCC{'i', 'i', 'n', 'f'}
- boxTypeINFE = fourCC{'i', 'n', 'f', 'e'}
- boxTypeIPRP = fourCC{'i', 'p', 'r', 'p'}
- boxTypeIPCO = fourCC{'i', 'p', 'c', 'o'}
- boxTypeISPE = fourCC{'i', 's', 'p', 'e'}
- boxTypePASP = fourCC{'p', 'a', 's', 'p'}
- boxTypeAV1C = fourCC{'a', 'v', '1', 'C'}
- boxTypePIXI = fourCC{'p', 'i', 'x', 'i'}
- boxTypeIPMA = fourCC{'i', 'p', 'm', 'a'}
-
- itemTypeMIF1 = fourCC{'m', 'i', 'f', '1'}
- itemTypeAVIF = fourCC{'a', 'v', 'i', 'f'}
- itemTypeMIAF = fourCC{'m', 'i', 'a', 'f'}
- itemTypePICT = fourCC{'p', 'i', 'c', 't'}
- itemTypeMIME = fourCC{'m', 'i', 'm', 'e'}
- itemTypeURI = fourCC{'u', 'r', 'i', ' '}
- itemTypeAV01 = fourCC{'a', 'v', '0', '1'}
-)
-
-func ulen(s string) uint32 {
- return uint32(len(s))
-}
-
-func bflag(b bool, pos uint8) uint8 {
- if b {
- return 1 << (pos - 1)
- } else {
- return 0
- }
-}
-
-func writeAll(w io.Writer, writers ...io.WriterTo) (err error) {
- for _, wt := range writers {
- _, err = wt.WriteTo(w)
- if err != nil {
- return
- }
- }
- return
-}
-
-func writeBE(w io.Writer, chunks ...interface{}) (err error) {
- for _, v := range chunks {
- err = binary.Write(w, binary.BigEndian, v)
- if err != nil {
- return
- }
- }
- return
-}
-
-//----------------------------------------------------------------------
-
-type box struct {
- size uint32
- typ fourCC
-}
-
-func (b *box) Size() uint32 {
- return 8
-}
-
-func (b *box) WriteTo(w io.Writer) (n int64, err error) {
- err = writeBE(w, b.size, b.typ)
- return
-}
-
-//----------------------------------------------------------------------
-
-type fullBox struct {
- box
- version uint8
- flags uint32
-}
-
-func (b *fullBox) Size() uint32 {
- return 12
-}
-
-func (b *fullBox) WriteTo(w io.Writer) (n int64, err error) {
- if _, err = b.box.WriteTo(w); err != nil {
- return
- }
- versionAndFlags := (uint32(b.version) << 24) | (b.flags & 0xffffff)
- err = writeBE(w, versionAndFlags)
- return
-}
-
-//----------------------------------------------------------------------
-
-// File Type Box
-type boxFTYP struct {
- box
- majorBrand fourCC
- minorVersion uint32
- compatibleBrands []fourCC
-}
-
-func (b *boxFTYP) Size() uint32 {
- return b.box.Size() +
- 4 /*major_brand*/ + 4 /*minor_version*/ + uint32(len(b.compatibleBrands))*4
-}
-
-func (b *boxFTYP) WriteTo(w io.Writer) (n int64, err error) {
- b.size = b.Size()
- b.typ = boxTypeFTYP
- if _, err = b.box.WriteTo(w); err != nil {
- return
- }
- err = writeBE(w, b.majorBrand, b.minorVersion, b.compatibleBrands)
- return
-}
-
-//----------------------------------------------------------------------
-
-// Media Data Box
-type boxMDAT struct {
- box
- data []byte
-}
-
-func (b *boxMDAT) Size() uint32 {
- return b.box.Size() + uint32(len(b.data))
-}
-
-func (b *boxMDAT) WriteTo(w io.Writer) (n int64, err error) {
- b.size = b.Size()
- b.typ = boxTypeMDAT
- if _, err = b.box.WriteTo(w); err != nil {
- return
- }
- _, err = w.Write(b.data)
- return
-}
-
-//----------------------------------------------------------------------
-
-// The Meta box
-type boxMETA struct {
- fullBox
- theHandler boxHDLR
- primaryResource boxPITM
- itemLocations boxILOC
- itemInfos boxIINF
- itemProps boxIPRP
-}
-
-func (b *boxMETA) Size() uint32 {
- return b.fullBox.Size() + b.theHandler.Size() + b.primaryResource.Size() +
- b.itemLocations.Size() + b.itemInfos.Size() + b.itemProps.Size()
-}
-
-func (b *boxMETA) WriteTo(w io.Writer) (n int64, err error) {
- b.size = b.Size()
- b.typ = boxTypeMETA
- if _, err = b.fullBox.WriteTo(w); err != nil {
- return
- }
- err = writeAll(w, &b.theHandler, &b.primaryResource, &b.itemLocations,
- &b.itemInfos, &b.itemProps)
- return
-}
-
-//----------------------------------------------------------------------
-
-// Handler Reference Box
-type boxHDLR struct {
- fullBox
- preDefined uint32
- handlerType fourCC
- reserved [3]uint32
- name string
-}
-
-func (b *boxHDLR) Size() uint32 {
- return b.fullBox.Size() +
- 4 /*pre_defined*/ + 4 /*handler_type*/ + 12 /*reserved*/ +
- ulen(b.name) + 1 /*\0*/
-}
-
-func (b *boxHDLR) WriteTo(w io.Writer) (n int64, err error) {
- b.size = b.Size()
- b.typ = boxTypeHDLR
- if _, err = b.fullBox.WriteTo(w); err != nil {
- return
- }
- err = writeBE(w, b.preDefined, b.handlerType, b.reserved, []byte(b.name), []byte{0})
- return
-}
-
-//----------------------------------------------------------------------
-
-// Primary Item Box
-type boxPITM struct {
- fullBox
- itemID uint16
-}
-
-func (b *boxPITM) Size() uint32 {
- return b.fullBox.Size() + 2 /*item_ID*/
-}
-
-func (b *boxPITM) WriteTo(w io.Writer) (n int64, err error) {
- b.size = b.Size()
- b.typ = boxTypePITM
- if _, err = b.fullBox.WriteTo(w); err != nil {
- return
- }
- err = writeBE(w, b.itemID)
- return
-}
-
-//----------------------------------------------------------------------
-
-// The Item Location Box
-type boxILOC struct {
- fullBox
- offsetSize uint8 // 4 bits
- lengthSize uint8 // 4 bits
- baseOffsetSize uint8 // 4 bits
- reserved uint8 // 4 bits
- itemCount uint16
- items []boxILOCItem
-}
-
-func (b *boxILOC) Size() uint32 {
- size := b.fullBox.Size() + 1 /*offset_size + length_size*/ +
- 1 /*base_offset_size + reserved*/ + 2 /*item_count*/
- for _, i := range b.items {
- size += 2 /*item_ID*/ + 2 /*data_reference_index*/ + uint32(b.baseOffsetSize) +
- 2 /*extent_count*/ + uint32(len(i.extents))*uint32(b.offsetSize+b.lengthSize)
- }
- return size
-}
-
-func (b *boxILOC) WriteTo(w io.Writer) (n int64, err error) {
- b.size = b.Size()
- b.typ = boxTypeILOC
- b.itemCount = uint16(len(b.items))
- if _, err = b.fullBox.WriteTo(w); err != nil {
- return
- }
- offsetSizeAndLengthSize := (b.offsetSize << 4) | (b.lengthSize & 0xf)
- baseOffsetSizeAndReserved := (b.baseOffsetSize << 4) | (b.reserved & 0xf)
- err = writeBE(w, offsetSizeAndLengthSize, baseOffsetSizeAndReserved, b.itemCount)
- if err != nil {
- return
- }
- for _, i := range b.items {
- err = i.write(w, b.baseOffsetSize, b.offsetSize, b.lengthSize)
- if err != nil {
- return
- }
- }
- return
-}
-
-type boxILOCItem struct {
- itemID uint16
- dataReferenceIndex uint16
- baseOffset uint64 // 0, 32 or 64 bits
- extentCount uint16
- extents []boxILOCItemExtent