Ignite/frame/stream.go

193 lines
3.7 KiB
Go

package frame
import (
"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
lock atomic.Bool
}
func NewStream(properties StreamProperties) (*Stream, chan Frame) {
s := &Stream{
properties: properties,
channel: make(chan Frame),
}
return s, s.channel
}
type StreamProperties struct {
// Width could be not populated until the first frame is read. Frame can contain different settings.
Width int
// Height could be not populated until the first frame is read. Frame can contain different settings.
Height int
// PixelAspectRatio could be not populated until the first frame is read. Frame can contain different settings.
PixelAspectRatio utilities.Ratio
// ColorSpace could be not populated until the first frame is read. Frame can contain different settings.
ColorSpace color.Space
FrameRate utilities.Ratio
FullColorRange bool
}
func (p StreamProperties) FrameProperties() Properties {
return Properties{
Width: p.Width,
Height: p.Height,
PixelAspectRatio: p.PixelAspectRatio,
ColorSpace: p.ColorSpace,
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 end < start {
return nil
}
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
}
// Split produces several Stream across each split point, and locks the input.
// For example, splits = [2, 5, 20] will produce 4 Stream, [0-1], [2-4], [5-19], [20-...]
func (s *Stream) Split(splits ...int) []*Stream {
if len(splits) == 0 {
return []*Stream{s}
} else if !s.Lock() {
return nil
}
slice := make([]*Stream, len(splits)+1)
for i := range slice {
slice[i], _ = NewStream(s.Properties())
}
var index int
go func() {
defer func() {
//empty source channel
for range s.channel {
}
}()
defer func() {
for _, sC := range slice {
close(sC.channel)
}
}()
for f := range s.channel {
for len(splits) > 0 && index >= splits[0] {
close(slice[0].channel)
splits = splits[1:]
slice = slice[1:]
}
slice[0].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
}