Rework libaom internals, move y4m, add OBU writer

This commit is contained in:
DataHoarder 2022-09-16 05:43:46 +02:00
parent c804c2e337
commit 1bea842cb1
Signed by: DataHoarder
SSH key fingerprint: SHA256:EnPQOqPpbCa7nzalCEJY2sd9iPluFIBuJu2rDFalACI
4 changed files with 86 additions and 63 deletions

54
bitstream/obu/writer.go Normal file
View file

@ -0,0 +1,54 @@
package obu
import (
"encoding/binary"
"git.gammaspectra.live/S.O.N.G/Ignite/utilities"
"io"
)
type Writer struct {
w io.Writer
}
func NewWriter(w io.Writer, width, height int, fourCC uint32, frameRate utilities.Ratio) (*Writer, error) {
if err := binary.Write(w, binary.LittleEndian, struct {
Magic [4]byte
Version uint16
HeaderSize uint16
FourCC uint32
Width uint16
Height uint16
Denominator uint32
Numerator uint32
Length uint32
Unused uint32
}{
Magic: [4]byte{'D', 'K', 'I', 'F'},
Version: 0,
HeaderSize: 32,
FourCC: fourCC,
Width: uint16(width),
Height: uint16(height),
Denominator: uint32(frameRate.Numerator),
Numerator: uint32(frameRate.Denominator),
Length: 0,
}); err != nil {
return nil, err
}
return &Writer{
w: w,
}, nil
}
func (w *Writer) WriteFrameBytes(pts uint64, data []byte) error {
if err := binary.Write(w.w, binary.LittleEndian, uint32(len(data))); err != nil {
return err
} else if err = binary.Write(w.w, binary.LittleEndian, uint64(pts)); err != nil {
return err
} else if _, err = w.w.Write(data); err != nil {
return err
}
return nil
}

View file

@ -6,11 +6,11 @@ package libaom
*/
import "C"
import (
"encoding/binary"
"errors"
"fmt"
"git.gammaspectra.live/S.O.N.G/Ignite/bitstream/obu"
"git.gammaspectra.live/S.O.N.G/Ignite/bitstream/y4m"
"git.gammaspectra.live/S.O.N.G/Ignite/frame"
"git.gammaspectra.live/S.O.N.G/Ignite/y4m"
"io"
"runtime"
"strconv"
@ -19,7 +19,7 @@ import (
)
type Encoder struct {
w io.Writer
w *obu.Writer
cleaned atomic.Bool
cfg C.aom_codec_enc_cfg_t
codec C.aom_codec_ctx_t
@ -39,9 +39,7 @@ const (
)
func NewEncoder(w io.Writer, stream *y4m.Stream, settings map[string]any) (*Encoder, error) {
e := &Encoder{
w: w,
}
e := &Encoder{}
var aomErr C.aom_codec_err_t
@ -50,29 +48,22 @@ func NewEncoder(w io.Writer, stream *y4m.Stream, settings map[string]any) (*Enco
return nil, errors.New("unsupported codec")
}
globalConfig := struct {
usage C.uint
speed C.int
}{
usage: UsageGoodQuality,
}
e.cfg.g_usage = C.uint(getSettingInt(settings, "usage", int(UsageGoodQuality)))
globalConfig.usage = C.uint(getSettingInt(settings, "usage", int(globalConfig.usage)))
if getSettingBool(settings, "good", false) {
globalConfig.usage = UsageGoodQuality
e.cfg.g_usage = UsageGoodQuality
}
if getSettingBool(settings, "rt", false) {
globalConfig.usage = UsageRealtime
e.cfg.g_usage = UsageRealtime
}
if getSettingBool(settings, "allintra", false) {
globalConfig.usage = UsageAllIntra
e.cfg.g_usage = UsageAllIntra
}
globalConfig.speed = C.int(getSettingInt(settings, "cpu-used", int(globalConfig.speed)))
var imageFormat C.aom_img_fmt_t
var flags C.aom_codec_flags_t
if aomErr = C.aom_codec_enc_config_default(encoder, &e.cfg, globalConfig.usage); aomErr != 0 {
if aomErr = C.aom_codec_enc_config_default(encoder, &e.cfg, e.cfg.g_usage); aomErr != 0 {
return nil, errors.New("failed to get default codec config")
}
@ -91,10 +82,6 @@ func NewEncoder(w io.Writer, stream *y4m.Stream, settings map[string]any) (*Enco
}
if stream.ColorFormat().BitDepth > 8 {
imageFormat |= C.AOM_IMG_FMT_HIGHBITDEPTH
}
e.cfg.g_input_bit_depth = C.uint(stream.ColorFormat().BitDepth)
e.cfg.g_bit_depth = C.aom_bit_depth_t(stream.ColorFormat().BitDepth)
@ -103,6 +90,13 @@ func NewEncoder(w io.Writer, stream *y4m.Stream, settings map[string]any) (*Enco
e.cfg.g_profile = 2
}
if e.cfg.g_input_bit_depth > 8 {
imageFormat |= C.AOM_IMG_FMT_HIGHBITDEPTH
}
if e.cfg.g_bit_depth > 8 {
flags |= C.AOM_CODEC_USE_HIGHBITDEPTH
}
width, height := stream.Resolution()
if e.raw = (*C.aom_image_t)(C.malloc(C.size_t(unsafe.Sizeof(C.aom_image_t{})))); e.raw == nil {
@ -142,10 +136,20 @@ func NewEncoder(w io.Writer, stream *y4m.Stream, settings map[string]any) (*Enco
//TODO: find all settings not set on AV1 encoder and place them on e.cfg
if aomErr = C.aom_codec_enc_init_ver(&e.codec, encoder, &e.cfg, 0, C.AOM_ENCODER_ABI_VERSION); aomErr != 0 {
if aomErr = C.aom_codec_enc_init_ver(&e.codec, encoder, &e.cfg, flags, C.AOM_ENCODER_ABI_VERSION); aomErr != 0 {
return nil, fmt.Errorf("failed to initialize encoder: %s", C.GoString(e.codec.err_detail))
}
if stream.ColorRange() {
if aomErr = C.aom_codec_control_uint(&e.codec, C.AV1E_SET_COLOR_RANGE, 1); aomErr != 0 {
return nil, fmt.Errorf("failed to set color range")
}
} else {
if aomErr = C.aom_codec_control_uint(&e.codec, C.AV1E_SET_COLOR_RANGE, 0); aomErr != 0 {
return nil, fmt.Errorf("failed to set color range")
}
}
for k, v := range settings {
if err := func() error {
var strVal *C.char
@ -180,28 +184,8 @@ func NewEncoder(w io.Writer, stream *y4m.Stream, settings map[string]any) (*Enco
}
}
if err := binary.Write(e.w, binary.LittleEndian, struct {
Magic [4]byte
Version uint16
HeaderSize uint16
FourCC uint32
Width uint16
Height uint16
Denominator uint32
Numerator uint32
Length uint32
Unused uint32
}{
Magic: [4]byte{'D', 'K', 'I', 'F'},
Version: 0,
HeaderSize: 32,
FourCC: 0x31305641,
Width: uint16(e.cfg.g_w),
Height: uint16(e.cfg.g_h),
Denominator: uint32(e.cfg.g_timebase.den),
Numerator: uint32(e.cfg.g_timebase.num),
Length: 0,
}); err != nil {
var err error
if e.w, err = obu.NewWriter(w, width, height, 0x31305641, stream.FrameRate()); err != nil {
return nil, err
}
@ -263,22 +247,7 @@ func (e *Encoder) encodeFrame(pts int64, raw *C.aom_image_t) (pkts int, err erro
pkts++
if pkt.kind == C.AOM_CODEC_CX_FRAME_PKT {
dataFrameSz := C.aom_get_pkt_sz(pkt)
dataFramePts := C.aom_get_pkt_pts(pkt)
buf := make([]byte, 4)
binary.LittleEndian.PutUint32(buf, uint32(dataFrameSz))
if _, err = e.w.Write(buf); err != nil {
return pkts, err
}
binary.LittleEndian.PutUint32(buf, uint32(dataFramePts&0xFFFFFFFF))
if _, err = e.w.Write(buf); err != nil {
return pkts, err
}
binary.LittleEndian.PutUint32(buf, uint32(dataFramePts>>32))
if _, err = e.w.Write(buf); err != nil {
return pkts, err
}
if _, err = e.w.Write(unsafe.Slice((*byte)(C.aom_get_pkt_buf(pkt)), int(dataFrameSz))); err != nil {
if err = e.w.WriteFrameBytes(uint64(C.aom_get_pkt_pts(pkt)), unsafe.Slice((*byte)(C.aom_get_pkt_buf(pkt)), int(C.aom_get_pkt_sz(pkt)))); err != nil {
return pkts, err
}
}

View file

@ -11,8 +11,8 @@ import "C"
import (
"errors"
"fmt"
"git.gammaspectra.live/S.O.N.G/Ignite/bitstream/y4m"
"git.gammaspectra.live/S.O.N.G/Ignite/frame"
"git.gammaspectra.live/S.O.N.G/Ignite/y4m"
"io"
"runtime"
"strconv"