package frameserver import ( "git.gammaspectra.live/S.O.N.G/Ignite/decoder/y4m" "git.gammaspectra.live/S.O.N.G/Ignite/frame" "io" "sync" ) type FrameServer struct { readerLock sync.Mutex decoder *y4m.Decoder } func New(reader io.ReadSeeker, cachedFrameSeekTable []int64) *FrameServer { s := &FrameServer{} var err error settings := make(map[string]any) settings["seek_table"] = cachedFrameSeekTable if s.decoder, err = y4m.NewDecoder(reader, settings); err != nil { return nil } return s } func NewXZCompressed(reader io.ReadSeeker, cachedFrameSeekTable []int64) *FrameServer { s := &FrameServer{} var err error settings := make(map[string]any) settings["seek_table"] = cachedFrameSeekTable if s.decoder, err = y4m.NewXZCompressedDecoder(reader, settings); err != nil { return nil } return s } func (s *FrameServer) GetFrameSeekTable() []int64 { s.readerLock.Lock() defer s.readerLock.Unlock() return s.decoder.GetFrameSeekTable() } // GetAllFrames gets all frames from start to end. Locks on every iteration. See GetAllFramesLock func (s *FrameServer) GetAllFrames() *frame.Stream { frameNumber := 0 stream, channel := frame.NewStream(s.decoder.Properties()) go func() { defer close(channel) for { if f, err := s.GetFrame(frameNumber); err != nil { return } else { channel <- f } frameNumber++ } }() return stream } // GetAllFramesLock gets all frames from start to end. Locks on every iteration. See GetAllFramesLock func (s *FrameServer) GetAllFramesLock() *frame.Stream { s.readerLock.Lock() defer s.readerLock.Unlock() frameNumber := 0 if err := s.decoder.SeekToFrame(frameNumber); err != nil { return nil } else { stream, channel := frame.NewStream(s.decoder.Properties()) go func() { defer close(channel) for { if f, err := s.decoder.Decode(); err != nil { return } else { channel <- f } frameNumber++ } }() return stream } } // GetFrames gets a range of frames, like a slice [0, 1] will return just one frame.Frame. Locks on every iteration. See GetFramesLock func (s *FrameServer) GetFrames(startFrameNumber, endFrameNumber int) *frame.Stream { if endFrameNumber < startFrameNumber { return nil } frameNumber := startFrameNumber stream, channel := frame.NewStream(s.decoder.Properties()) go func() { defer close(channel) for frameNumber < endFrameNumber { if f, err := s.GetFrame(frameNumber); err != nil { return } else { channel <- f } frameNumber++ } }() return stream } // GetFramesLock gets a range of frames, but locks during all of them. See GetFrames func (s *FrameServer) GetFramesLock(startFrameNumber, endFrameNumber int) *frame.Stream { if endFrameNumber < startFrameNumber { return nil } s.readerLock.Lock() defer s.readerLock.Unlock() frameNumber := startFrameNumber //todo: spawn new frame.Stream or just use GetFrame()? if err := s.decoder.SeekToFrame(frameNumber); err != nil { return nil } else { stream, channel := frame.NewStream(s.decoder.Properties()) go func() { defer close(channel) for frameNumber < endFrameNumber { if f, err := s.decoder.Decode(); err != nil { return } else { channel <- f } frameNumber++ } }() return stream } } func (s *FrameServer) GetFrame(frameNumber int) (f frame.Frame, err error) { s.readerLock.Lock() defer s.readerLock.Unlock() if err = s.decoder.SeekToFrame(frameNumber); err != nil { return nil, err } else { return s.decoder.Decode() } }