Change frame.Stream, include methods to Slice, Copy, or Sample frame.Stream
This commit is contained in:
parent
487b9f16d2
commit
fe40f4da7e
10
README.md
Normal file
10
README.md
Normal 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.
|
|
@ -1,4 +1,4 @@
|
|||
package colorformat
|
||||
package color
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
11
color/matrix.go
Normal file
11
color/matrix.go
Normal file
|
@ -0,0 +1,11 @@
|
|||
package color
|
||||
|
||||
type MatrixCoefficients int
|
||||
|
||||
const (
|
||||
MatrixBT709 MatrixCoefficients = iota
|
||||
MatrixBT601
|
||||
MatrixBT2020NCL
|
||||
MatrixBT2020CL
|
||||
MatrixSMPTE285
|
||||
)
|
12
color/primary.go
Normal file
12
color/primary.go
Normal 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
15
color/transfer.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
package color
|
||||
|
||||
type Transfer int
|
||||
|
||||
const (
|
||||
TransferBT709 Transfer = iota
|
||||
TransferBT601
|
||||
TransferLINEAR
|
||||
TransferSRGB
|
||||
TransferBT2020_10bit
|
||||
TransferBT2020_12bit
|
||||
TransferSMPTE240
|
||||
TransferSMPTE431
|
||||
TransferSMPTE432
|
||||
)
|
|
@ -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 {
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
121
frame/stream.go
121
frame/stream.go
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue