Change frame.Stream, include methods to Slice, Copy, or Sample frame.Stream

This commit is contained in:
DataHoarder 2022-11-11 07:30:58 +01:00
parent 487b9f16d2
commit fe40f4da7e
Signed by: DataHoarder
SSH key fingerprint: SHA256:OLTRf6Fl87G52SiR7sWLGNzlJt4WOX+tfI2yxo0z7xk
11 changed files with 194 additions and 26 deletions

10
README.md Normal file
View file

@ -0,0 +1,10 @@
# Supported
* y4m pipes
* 4:4:4, 4:2:0, and probably 4:2:2 and 4:0:0.
* 8, 10, 12 bit depth. Probably 14 and 16 as well, and lower than 8 (but why).
* TODO: make list per encoder and decoder.
# TODO
* No SAR/PAR handling.
* No color primary / transfer / matrix coefficients handling.

View file

@ -1,4 +1,4 @@
package colorformat
package color
import (
"encoding/binary"

11
color/matrix.go Normal file
View file

@ -0,0 +1,11 @@
package color
type MatrixCoefficients int
const (
MatrixBT709 MatrixCoefficients = iota
MatrixBT601
MatrixBT2020NCL
MatrixBT2020CL
MatrixSMPTE285
)

12
color/primary.go Normal file
View file

@ -0,0 +1,12 @@
package color
type Primary int
const (
PrimaryBT709 Primary = iota
PrimaryBT601
PrimaryBT2020
PrimarySMPTE240
PrimarySMPTE431
PrimarySMPTE432
)

15
color/transfer.go Normal file
View file

@ -0,0 +1,15 @@
package color
type Transfer int
const (
TransferBT709 Transfer = iota
TransferBT601
TransferLINEAR
TransferSRGB
TransferBT2020_10bit
TransferBT2020_12bit
TransferSMPTE240
TransferSMPTE431
TransferSMPTE432
)

View file

@ -8,7 +8,7 @@ import "C"
import (
"errors"
"fmt"
"git.gammaspectra.live/S.O.N.G/Ignite/colorformat"
"git.gammaspectra.live/S.O.N.G/Ignite/color"
"git.gammaspectra.live/S.O.N.G/Ignite/frame"
"git.gammaspectra.live/S.O.N.G/Ignite/utilities"
"git.gammaspectra.live/S.O.N.G/Ignite/utilities/ivfreader"
@ -44,6 +44,7 @@ func Version() string {
func NewDecoder(r io.Reader, settings map[string]any) (d *Decoder, err error) {
d = &Decoder{}
d.properties.PixelAspectRatio = utilities.Ratio{Numerator: 1, Denominator: 1}
if d.r, d.h, err = ivfreader.NewWith(r); err != nil {
return nil, err
@ -153,13 +154,13 @@ func (d *Decoder) pictureToFrame() (frame.Frame, error) {
switch d.picture.p.layout {
case C.DAV1D_PIXEL_LAYOUT_I400:
properties.ColorFormat.Subsampling = colorformat.SubsamplingScheme{J: 4, A: 0, B: 0}
properties.ColorFormat.Subsampling = color.SubsamplingScheme{J: 4, A: 0, B: 0}
case C.DAV1D_PIXEL_LAYOUT_I420:
properties.ColorFormat.Subsampling = colorformat.SubsamplingScheme{J: 4, A: 2, B: 0}
properties.ColorFormat.Subsampling = color.SubsamplingScheme{J: 4, A: 2, B: 0}
case C.DAV1D_PIXEL_LAYOUT_I422:
properties.ColorFormat.Subsampling = colorformat.SubsamplingScheme{J: 4, A: 2, B: 2}
properties.ColorFormat.Subsampling = color.SubsamplingScheme{J: 4, A: 2, B: 2}
case C.DAV1D_PIXEL_LAYOUT_I444:
properties.ColorFormat.Subsampling = colorformat.SubsamplingScheme{J: 4, A: 4, B: 4}
properties.ColorFormat.Subsampling = color.SubsamplingScheme{J: 4, A: 4, B: 4}
}
if d.picture.p.layout != C.DAV1D_PIXEL_LAYOUT_I400 {
@ -177,6 +178,12 @@ func (d *Decoder) pictureToFrame() (frame.Frame, error) {
}
properties.ColorFormat.BitDepth = byte(bitDepth)
properties.FullColorRange = false
if d.picture.seq_hdr.color_range == 1 {
//TODO check
properties.FullColorRange = true
}
defer C.dav1d_picture_unref(&d.picture)
@ -216,14 +223,14 @@ func (d *Decoder) pictureToFrame() (frame.Frame, error) {
}
func (d *Decoder) DecodeStream() *frame.Stream {
stream := frame.NewStream(d.properties)
stream, channel := frame.NewStream(d.properties)
go func() {
defer close(stream.Channel)
defer close(channel)
for {
if f, err := d.Decode(); err != nil {
return
} else {
stream.Channel <- f
channel <- f
}
}
}()
@ -231,6 +238,10 @@ func (d *Decoder) DecodeStream() *frame.Stream {
}
func (d *Decoder) Decode() (frame.Frame, error) {
if f := d.firstFrame; f != nil {
d.firstFrame = nil
return f, nil
}
var ret C.int
for {
if ret = d.decodeFrame(); ret == -ErrEAGAIN {

View file

@ -3,7 +3,7 @@ package y4m
import (
"errors"
"fmt"
"git.gammaspectra.live/S.O.N.G/Ignite/colorformat"
"git.gammaspectra.live/S.O.N.G/Ignite/color"
"git.gammaspectra.live/S.O.N.G/Ignite/frame"
"io"
"strconv"
@ -81,14 +81,14 @@ func (s *Stream) Decode() (frame.Frame, error) {
return f, err
}
func (s *Stream) DecodeStream() *frame.Stream {
stream := frame.NewStream(s.properties)
stream, channel := frame.NewStream(s.properties)
go func() {
defer close(stream.Channel)
defer close(channel)
for {
if f, err := s.Decode(); err != nil {
return
} else {
stream.Channel <- f
channel <- f
}
}
}()
@ -256,7 +256,7 @@ func (s *Stream) parseParameters() (err error) {
return err
}
case ParameterColorFormat:
if s.properties.ColorFormat, err = colorformat.NewColorFormatFromString(values[0]); err != nil {
if s.properties.ColorFormat, err = color.NewColorFormatFromString(values[0]); err != nil {
return err
}
case ParameterExtension:

View file

@ -190,7 +190,7 @@ func NewEncoder(w io.Writer, properties frame.StreamProperties, settings map[str
}
func (e *Encoder) EncodeStream(stream *frame.Stream) error {
for f := range stream.Channel {
for f := range stream.Channel() {
if err := e.Encode(f); err != nil {
return err
}

View file

@ -158,7 +158,7 @@ func NewEncoder(w io.Writer, properties frame.StreamProperties, settings map[str
}
func (e *Encoder) EncodeStream(stream *frame.Stream) error {
for f := range stream.Channel {
for f := range stream.Channel() {
if err := e.Encode(f); err != nil {
return err
}

View file

@ -1,7 +1,7 @@
package frame
import (
"git.gammaspectra.live/S.O.N.G/Ignite/colorformat"
"git.gammaspectra.live/S.O.N.G/Ignite/color"
"git.gammaspectra.live/S.O.N.G/Ignite/utilities"
)
@ -20,8 +20,12 @@ type Frame interface {
type TypedFrame[T AllowedFrameTypes] interface {
Frame
GetNative(x, y int) (Y T, Cb T, Cr T)
// GetNativeLuma also known as Y
GetNativeLuma() []T
// GetNativeCb also known as U
GetNativeCb() []T
// GetNativeCr also known as V
GetNativeCr() []T
}
@ -30,6 +34,6 @@ type Properties struct {
Width int
Height int
PixelAspectRatio utilities.Ratio
ColorFormat colorformat.ColorFormat
ColorFormat color.ColorFormat
FullColorRange bool
}

View file

@ -1,20 +1,23 @@
package frame
import (
"git.gammaspectra.live/S.O.N.G/Ignite/colorformat"
"git.gammaspectra.live/S.O.N.G/Ignite/color"
"git.gammaspectra.live/S.O.N.G/Ignite/utilities"
"sync/atomic"
)
type Stream struct {
Properties StreamProperties
Channel chan Frame
properties StreamProperties
channel chan Frame
lock atomic.Bool
}
func NewStream(properties StreamProperties) *Stream {
return &Stream{
Properties: properties,
Channel: make(chan Frame),
func NewStream(properties StreamProperties) (*Stream, chan Frame) {
s := &Stream{
properties: properties,
channel: make(chan Frame),
}
return s, s.channel
}
type StreamProperties struct {
@ -25,7 +28,7 @@ type StreamProperties struct {
// PixelAspectRatio could be not populated until the first frame is read. Frame can contain different settings.
PixelAspectRatio utilities.Ratio
// ColorFormat could be not populated until the first frame is read. Frame can contain different settings.
ColorFormat colorformat.ColorFormat
ColorFormat color.ColorFormat
FrameRate utilities.Ratio
FullColorRange bool
}
@ -39,3 +42,105 @@ func (p StreamProperties) FrameProperties() Properties {
FullColorRange: p.FullColorRange,
}
}
func (s *Stream) Properties() StreamProperties {
return s.properties
}
// Channel gets the Frame channel, and locks the input
func (s *Stream) Channel() chan Frame {
if s.Lock() {
return s.channel
}
return nil
}
// Lock locks the input, and returns success
func (s *Stream) Lock() bool {
return !s.lock.Swap(true)
}
// Unlock unlocks the input, and returns the old value
func (s *Stream) Unlock() bool {
return s.lock.Swap(false)
}
// Copy copies the stream n times
func (s *Stream) Copy(n int) []*Stream {
if !s.Lock() {
return nil
}
slice := make([]*Stream, n)
for i := range slice {
slice[i], _ = NewStream(s.Properties())
}
go func() {
defer func() {
for _, sliceItem := range slice {
close(sliceItem.channel)
}
}()
for f := range s.channel {
for _, sliceItem := range slice {
sliceItem.channel <- f
}
}
}()
return slice
}
// Slice produces a slice of the stream, and locks the input
func (s *Stream) Slice(start, end int) *Stream {
if !s.Lock() {
return nil
}
slice, channel := NewStream(s.Properties())
var index int
go func() {
defer func() {
//empty source channel
for range s.channel {
}
}()
defer close(channel)
for f := range s.channel {
if index >= end {
break
}
if index >= start {
channel <- f
}
index++
}
}()
return slice
}
// Sample samples frames every each Frame, and locks the input
func (s *Stream) Sample(each int) *Stream {
if !s.Lock() {
return nil
}
slice, channel := NewStream(s.Properties())
var index int
go func() {
defer close(channel)
for f := range s.channel {
if index%each == 0 {
channel <- f
}
index++
}
}()
return slice
}