Rework libaom internals, move y4m, add OBU writer
This commit is contained in:
parent
c804c2e337
commit
1bea842cb1
54
bitstream/obu/writer.go
Normal file
54
bitstream/obu/writer.go
Normal 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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in a new issue