Skip to content

Commit

Permalink
Added software resample context
Browse files Browse the repository at this point in the history
  • Loading branch information
asticode committed Dec 1, 2024
1 parent ca1f6cd commit b5f2934
Show file tree
Hide file tree
Showing 7 changed files with 467 additions and 65 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ Examples are located in the [examples](examples) directory and mirror as much as
|Hardware Decoding|[see](examples/hardware_decoding/main.go)|[see](https://github.com/FFmpeg/FFmpeg/blob/n7.0/doc/examples/hw_decode.c)
|Hardware Encoding|[see](examples/hardware_encoding/main.go)|[see](https://github.com/FFmpeg/FFmpeg/blob/n7.0/doc/examples/vaapi_encode.c)
|Remuxing|[see](examples/remuxing/main.go)|[see](https://github.com/FFmpeg/FFmpeg/blob/n7.0/doc/examples/remuxing.c)
|Scaling|[see](examples/scaling/main.go)|[see](https://github.com/FFmpeg/FFmpeg/blob/n7.0/doc/examples/scaling_video.c)
|Resampling audio|[see](examples/resampling_audio/main.go)|[see](https://github.com/FFmpeg/FFmpeg/blob/n7.0/doc/examples/resample_audio.c)
|Scaling video|[see](examples/scaling_video/main.go)|[see](https://github.com/FFmpeg/FFmpeg/blob/n7.0/doc/examples/scaling_video.c)
|Transcoding|[see](examples/transcoding/main.go)|[see](https://github.com/FFmpeg/FFmpeg/blob/n7.0/doc/examples/transcoding.c)

*Tip: you can use the video sample located in the `testdata` directory for your tests*
Expand Down
127 changes: 64 additions & 63 deletions astiav_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,11 @@ func (h *helper) close() {
type helperInput struct {
firstPkt *Packet
formatContext *FormatContext
lastFrame *Frame
lastFrames map[MediaType]*Frame
}

func newHelperInput() *helperInput {
return &helperInput{lastFrames: make(map[MediaType]*Frame)}
}

func (h *helper) inputFormatContext(name string, ifmt *InputFormat) (fc *FormatContext, err error) {
Expand Down Expand Up @@ -78,7 +82,7 @@ func (h *helper) inputFormatContext(name string, ifmt *InputFormat) (fc *FormatC

h.m.Lock()
if _, ok := h.inputs[name]; !ok {
h.inputs[name] = &helperInput{}
h.inputs[name] = newHelperInput()
}
h.inputs[name].formatContext = fc
h.m.Unlock()
Expand Down Expand Up @@ -118,127 +122,124 @@ func (h *helper) inputFirstPacket(name string) (pkt *Packet, err error) {
return
}

func (h *helper) inputLastFrame(name string, mediaType MediaType, ifmt *InputFormat) (f *Frame, err error) {
func (h *helper) inputLastFrame(name string, mediaType MediaType, ifmt *InputFormat) (*Frame, error) {
h.m.Lock()
i, ok := h.inputs[name]
if ok && i.lastFrame != nil {
h.m.Unlock()
return i.lastFrame, nil
if i, ok := h.inputs[name]; ok {
if len(i.lastFrames) > 0 {
f, ok := i.lastFrames[mediaType]
h.m.Unlock()
if ok {
return f, nil
}
return nil, fmt.Errorf("astiav_test: no last frame for media type %s", mediaType)
}
}
h.m.Unlock()

var fc *FormatContext
if fc, err = h.inputFormatContext(name, ifmt); err != nil {
err = fmt.Errorf("astiav_test: getting input format context failed: %w", err)
return
fc, err := h.inputFormatContext(name, ifmt)
if err != nil {
return nil, fmt.Errorf("astiav_test: getting input format context failed: %w", err)
}

var cc *CodecContext
var cs *Stream
for _, s := range fc.Streams() {
if s.CodecParameters().MediaType() != mediaType {
continue
type stream struct {
cc *CodecContext
s *Stream
}
streams := make(map[int]*stream)
mediaTypeFound := false
for _, v := range fc.Streams() {
s := &stream{s: v}
streams[v.Index()] = s

c := FindDecoder(v.CodecParameters().CodecID())
if c == nil {
return nil, errors.New("astiav_test: no codec")
}

cs = s
s.cc = AllocCodecContext(c)
if s.cc == nil {
return nil, errors.New("astiav_test: no codec context")
}
h.closer.Add(s.cc.Free)

c := FindDecoder(s.CodecParameters().CodecID())
if c == nil {
err = errors.New("astiav_test: no codec")
return
if err = s.s.CodecParameters().ToCodecContext(s.cc); err != nil {
return nil, fmt.Errorf("astiav_test: updating codec context failed: %w", err)
}

cc = AllocCodecContext(c)
if cc == nil {
err = errors.New("astiav_test: no codec context")
return
if err = s.cc.Open(c, nil); err != nil {
return nil, fmt.Errorf("astiav_test: opening codec context failed: %w", err)
}
h.closer.Add(cc.Free)

if err = cs.CodecParameters().ToCodecContext(cc); err != nil {
err = fmt.Errorf("astiav_test: updating codec context failed: %w", err)
return
if _, ok := h.inputs[name].lastFrames[s.cc.MediaType()]; !ok {
h.inputs[name].lastFrames[s.cc.MediaType()] = AllocFrame()
h.closer.Add(h.inputs[name].lastFrames[s.cc.MediaType()].Free)
}

if err = cc.Open(c, nil); err != nil {
err = fmt.Errorf("astiav_test: opening codec context failed: %w", err)
return
if s.cc.MediaType() == mediaType {
mediaTypeFound = true
}
break
}

if cs == nil {
err = errors.New("astiav_test: no valid video stream")
return
if !mediaTypeFound {
return nil, fmt.Errorf("astiav_test: no stream for media type %s", mediaType)
}

var pkt1 *Packet
if pkt1, err = h.inputFirstPacket(name); err != nil {
err = fmt.Errorf("astiav_test: getting input first packet failed: %w", err)
return
return nil, fmt.Errorf("astiav_test: getting input first packet failed: %w", err)
}

pkt2 := AllocPacket()
h.closer.Add(pkt2.Free)

f = AllocFrame()
f := AllocFrame()
h.closer.Add(f.Free)

lastFrame := AllocFrame()
h.closer.Add(lastFrame.Free)

pkts := []*Packet{pkt1}
for {
if err = fc.ReadFrame(pkt2); err != nil {
if errors.Is(err, ErrEof) || errors.Is(err, ErrEagain) {
if len(pkts) == 0 {
if err = f.Ref(lastFrame); err != nil {
err = fmt.Errorf("astiav_test: last refing frame failed: %w", err)
return
}
err = nil
break
}
} else {
err = fmt.Errorf("astiav_test: reading frame failed: %w", err)
return
return nil, fmt.Errorf("astiav_test: reading frame failed: %w", err)
}
} else {
pkts = append(pkts, pkt2)
}

for _, pkt := range pkts {
if pkt.StreamIndex() != cs.Index() {
s, ok := streams[pkt.StreamIndex()]
if !ok {
continue
}

if err = cc.SendPacket(pkt); err != nil {
err = fmt.Errorf("astiav_test: sending packet failed: %w", err)
return
if err = s.cc.SendPacket(pkt); err != nil {
return nil, fmt.Errorf("astiav_test: sending packet failed: %w", err)
}

for {
if err = cc.ReceiveFrame(f); err != nil {
if err = s.cc.ReceiveFrame(f); err != nil {
if errors.Is(err, ErrEof) || errors.Is(err, ErrEagain) {
err = nil
break
}
err = fmt.Errorf("astiav_test: receiving frame failed: %w", err)
return
return nil, fmt.Errorf("astiav_test: receiving frame failed: %w", err)
}

if err = lastFrame.Ref(f); err != nil {
err = fmt.Errorf("astiav_test: refing frame failed: %w", err)
return
h.m.Lock()
h.inputs[name].lastFrames[s.cc.MediaType()].Unref()
err = h.inputs[name].lastFrames[s.cc.MediaType()].Ref(f)
h.m.Unlock()
if err != nil {
return nil, fmt.Errorf("astiav_test: refing frame failed: %w", err)
}
}
}

pkts = []*Packet{}
}

h.m.Lock()
h.inputs[name].lastFrame = f
h.m.Unlock()
return
return h.inputs[name].lastFrames[mediaType], nil
}
4 changes: 3 additions & 1 deletion class_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,11 @@ func TestClassers(t *testing.T) {
defer os.RemoveAll(path)
ic2, err := AllocIOContext(1, true, nil, nil, nil)
require.NoError(t, err)
src := AllocSoftwareResampleContext()
ssc, err := CreateSoftwareScaleContext(1, 1, PixelFormatRgba, 2, 2, PixelFormatRgba, NewSoftwareScaleContextFlags())
require.NoError(t, err)

require.Equal(t, cl+12, len(classers.p))
require.Equal(t, cl+13, len(classers.p))
v, ok := classers.get(unsafe.Pointer(f1.c))
require.True(t, ok)
require.Equal(t, f1, v)
Expand All @@ -70,6 +71,7 @@ func TestClassers(t *testing.T) {
fmc2.CloseInput()
require.NoError(t, ic1.Close())
ic2.Free()
src.Free()
ssc.Free()
require.Equal(t, cl, len(classers.p))
}
Loading

0 comments on commit b5f2934

Please sign in to comment.