Skip to content

Commit

Permalink
Updated ffmpeg
Browse files Browse the repository at this point in the history
  • Loading branch information
djthorpe committed Mar 15, 2021
1 parent 55d359d commit 6310a21
Show file tree
Hide file tree
Showing 8 changed files with 180 additions and 49 deletions.
2 changes: 1 addition & 1 deletion cmd/audioid/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func (this *app) Decode(ctx context.Context, file gopi.MediaInput) error {
// Decode frames
return file.Read(ctx, streams[0:1], func(ctx gopi.MediaDecodeContext, packet gopi.MediaPacket) error {
return file.DecodeFrameIterator(ctx, packet, func(frame gopi.MediaFrame) error {
this.Print("Decoded", ctx.Frame(), " => ", frame)
this.Print("Decoded", ctx.Stream(), ctx.Frame(), " => ", frame)
return nil
})
})
Expand Down
10 changes: 6 additions & 4 deletions media.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,13 @@ type MediaCodec interface {
////////////////////////////////////////////////////////////////////////////////
// MEDIA STREAMS, PACKETS AND FRAMES

// MediaStream is a stream of packets from a media object
// MediaStream is a stream of packets from a media object, with a defined
// codec and audio or video profile
type MediaStream interface {
Index() int // Stream index
Flags() MediaFlag // Flags for the stream (Audio, Video, etc)
Codec() MediaCodec // Return codec and parameters
Index() int // Stream index
Flags() MediaFlag // Flags for the stream (Audio, Video, etc)
Codec() MediaCodec // Return codec and parameters
Profile() MediaProfile // Return audio or video profile for stream
}

// MediaPacket is a packet of data from a stream
Expand Down
89 changes: 88 additions & 1 deletion pkg/media/ffmpeg/audioprofile.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package ffmpeg

import (
"fmt"
"sync"

gopi "github.com/djthorpe/gopi/v3"
ffmpeg "github.com/djthorpe/gopi/v3/pkg/sys/ffmpeg"
Expand All @@ -13,9 +14,14 @@ import (
// TYPES

type AudioProfile struct {
sync.RWMutex

fmt ffmpeg.AVSampleFormat
rate uint
channels uint
layout ffmpeg.AVChannelLayout
ctx *ffmpeg.SwrContext
frame *ffmpeg.AVFrame
}

////////////////////////////////////////////////////////////////////////////////
Expand All @@ -35,17 +41,98 @@ func NewAudioProfile(fmt gopi.AudioFormat, rate uint, layout gopi.AudioChannelLa
this.channels = layout.Channels
}

// Return success
return this
}

func (this *AudioProfile) Dispose() error {
this.RWMutex.Lock()
defer this.RWMutex.Unlock()

// Free resources
if this.ctx != nil {
this.ctx.Free()
}
if this.frame != nil {
this.frame.Free()
}

// Release resources
this.ctx = nil
this.frame = nil

// Return success
return nil
}

////////////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
// PROPERTIES

func (this *AudioProfile) Flags() gopi.MediaFlag {
return gopi.MEDIA_FLAG_AUDIO
}

////////////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS

// Resample returns resampled audio frames which adhere to profile. The returned value
// is either a frame or nil if the resampling operation is still in process. After all
// frames have been called into this method a final call with nil is required
// to flush the last resampled frame.
func (this *AudioProfile) Resample(src *ffmpeg.AVFrame) (*ffmpeg.AVFrame, error) {
this.RWMutex.Lock()
defer this.RWMutex.Unlock()

// If Resample is called with nil then Flush frame
if src == nil && this.ctx != nil && this.frame != nil {
if err := this.ctx.FlushFrame(this.frame); err != nil {
return nil, err
} else if this.frame.NumSamples() > 0 {
return this.frame, nil
} else {
return nil, nil
}
}

// Return error if called with nil
if src == nil {
return nil, gopi.ErrBadParameter.WithPrefix("Resample")
}

// Check incoming frame parameters and create context
if src_fmt := src.SampleFormat(); src_fmt == ffmpeg.AV_SAMPLE_FMT_NONE {
return nil, gopi.ErrBadParameter.WithPrefix("Resample")
} else if this.ctx == nil {
// Initialize frame and context
if dest := ffmpeg.NewAudioFrame(this.fmt, int(this.rate), this.layout); dest == nil {
return nil, gopi.ErrInternalAppError.WithPrefix("Resample")
} else if ctx := ffmpeg.NewSwrContextEx(src_fmt, this.fmt, src.SampleRate(), int(this.rate), src.ChannelLayout(), this.layout); ctx == nil {
dest.Free()
return nil, gopi.ErrInternalAppError.WithPrefix("Resample")
} else if err := this.ctx.ConfigFrame(dest, src); err != nil {
dest.Free()
ctx.Free()
return nil, err
} else if ctx.IsInitialized() == false {
dest.Free()
ctx.Free()
return nil, gopi.ErrUnexpectedResponse.WithPrefix("Resample")
} else {
this.ctx = ctx
this.frame = dest
}
}

// Resample frame and return the frame if there is data else return nil
if err := this.ctx.ConvertFrame(this.frame, src); err != nil {
return nil, err
} else if this.frame.NumSamples() > 0 {
return this.frame, nil
} else {
return nil, nil
}
}

////////////////////////////////////////////////////////////////////////////////
// STRINGIFY

Expand Down
3 changes: 2 additions & 1 deletion pkg/media/ffmpeg/codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,10 @@ func NewCodecWithParameters(ctx *ffmpeg.AVCodecParameters) *codec {
}
}

func (this *codec) Release() {
func (this *codec) Release() error {
this.ctx = nil
this.codec = nil
return nil
}

////////////////////////////////////////////////////////////////////////////////
Expand Down
4 changes: 3 additions & 1 deletion pkg/media/ffmpeg/frame.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,12 @@ func (this *frame) String() string {
str := "<ffmpeg.mediaframe"
if flags := this.Flags(); flags != gopi.MEDIA_FLAG_NONE {
str += fmt.Sprint(" flags=", flags)
if flags&gopi.MEDIA_FLAG_VIDEO != 0 {
str += fmt.Sprint(" bounds=", this.Bounds())
}
}
if this.ctx != nil {
str += fmt.Sprint(" type=", this.ctx)
str += fmt.Sprint(" bounds=", this.Bounds())
}
return str + ">"
}
21 changes: 17 additions & 4 deletions pkg/media/ffmpeg/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@ type Manager struct {
gopi.Logger
sync.Mutex

in []*inputctx
out []*outputctx
in []*inputctx
out []*outputctx
audioprofile []*AudioProfile
}

////////////////////////////////////////////////////////////////////////////////
// NEW
// LIFECYCLE

func (this *Manager) New(gopi.Config) error {
if this.Logger == nil {
Expand Down Expand Up @@ -72,6 +73,13 @@ func (this *Manager) Dispose() error {
}
}

// Free all audio profiles
for _, profile := range this.audioprofile {
if err := profile.Dispose(); err != nil {
result = multierror.Append(result, err)
}
}

// Deinit
ffmpeg.AVFormatDeinit()

Expand All @@ -81,6 +89,7 @@ func (this *Manager) Dispose() error {
// Release resources
this.in = nil
this.out = nil
this.audioprofile = nil

// Return any errors
return result
Expand Down Expand Up @@ -248,7 +257,11 @@ func (this *Manager) ListCodecs(name string, flags gopi.MediaFlag) []gopi.MediaC
// PUBLIC METHODS - PROFILES

func (this *Manager) AudioProfile(fmt gopi.AudioFormat, rate uint, layout gopi.AudioChannelLayout) gopi.MediaProfile {
return NewAudioProfile(fmt, rate, layout)
profile := NewAudioProfile(fmt, rate, layout)
if profile != nil {
this.audioprofile = append(this.audioprofile, profile)
}
return profile
}

////////////////////////////////////////////////////////////////////////////////
Expand Down
69 changes: 63 additions & 6 deletions pkg/media/ffmpeg/stream.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,20 @@ package ffmpeg

import (
"fmt"
"sync"

gopi "github.com/djthorpe/gopi/v3"
ffmpeg "github.com/djthorpe/gopi/v3/pkg/sys/ffmpeg"
multierror "github.com/hashicorp/go-multierror"
)

////////////////////////////////////////////////////////////////////////////////
// TYPES

type stream struct {
sync.RWMutex
*AudioProfile

ctx *ffmpeg.AVStream
codec *codec
}
Expand All @@ -23,33 +28,64 @@ type stream struct {
// NewStream returns a stream object, can optiomally copy codec
// parameters from another stream if source is not set to nil
func NewStream(ctx *ffmpeg.AVStream, source *stream) *stream {
this := new(stream)

if ctx == nil {
return nil
}
if source == nil {
if codec := NewCodecWithParameters(ctx.CodecPar()); codec == nil {
return nil
} else {
return &stream{ctx, codec}
this.ctx = ctx
this.codec = codec
}
} else {
if codec := NewCodecWithParameters(source.ctx.CodecPar()); codec == nil {
return nil
} else {
return &stream{ctx, codec}
this.ctx = ctx
this.codec = codec
}
}

// Return success
return this
}

func (this *stream) Release() {
func (this *stream) Release() error {
this.RWMutex.Lock()
defer this.RWMutex.Unlock()

// Release
var result error
if this.codec != nil {
if err := this.codec.Release(); err != nil {
result = multierror.Append(result, err)
}
}
if this.AudioProfile != nil {
if err := this.AudioProfile.Dispose(); err != nil {
result = multierror.Append(result, err)
}
}

// Set instance variables to nil
this.ctx = nil
this.codec = nil
this.AudioProfile = nil

// Return any errors
return result
}

////////////////////////////////////////////////////////////////////////////////
// METHODS - STREAM

func (this *stream) Index() int {
this.RWMutex.RLock()
defer this.RWMutex.RUnlock()

if this.ctx == nil {
return -1
} else {
Expand All @@ -58,9 +94,11 @@ func (this *stream) Index() int {
}

func (this *stream) Flags() gopi.MediaFlag {
flags := gopi.MEDIA_FLAG_NONE
this.RWMutex.RLock()
defer this.RWMutex.RUnlock()

// Return NONE if released
flags := gopi.MEDIA_FLAG_NONE
if this.ctx == nil {
return flags
}
Expand All @@ -84,13 +122,27 @@ func (this *stream) Flags() gopi.MediaFlag {
}

func (this *stream) Codec() gopi.MediaCodec {
this.RWMutex.RLock()
defer this.RWMutex.RUnlock()

if this.ctx == nil {
return nil
} else {
return this.codec
}
}

func (this *stream) Profile() gopi.MediaProfile {
this.RWMutex.RLock()
defer this.RWMutex.RUnlock()

if this.AudioProfile != nil {
return this.AudioProfile
} else {
return nil
}
}

func (this *stream) NewContextWithOptions(options *ffmpeg.AVDictionary) *ffmpeg.AVCodecContext {
if this.ctx == nil || this.codec == nil {
return nil
Expand All @@ -114,8 +166,13 @@ func (this *stream) String() string {
str := "<ffmpeg.stream"
str += " index=" + fmt.Sprint(this.Index())
if flags := this.Flags(); flags != gopi.MEDIA_FLAG_NONE {
str += " flags=" + fmt.Sprint(flags)
str += fmt.Sprint(" flags=", flags)
}
if codec := this.Codec(); codec != nil {
str += fmt.Sprint(" codec=", codec)
}
if profile := this.Profile(); profile != nil {
str += fmt.Sprint(" profile=", profile)
}
str += " codec=" + fmt.Sprint(this.Codec())
return str + ">"
}
Loading

0 comments on commit 6310a21

Please sign in to comment.