general commit 25-02-2024 23:57

This commit is contained in:
harshabose
2025-02-25 23:58:03 +05:30
parent 186d3a1c0a
commit 25b181318e
9 changed files with 234 additions and 195 deletions

View File

@@ -1,26 +0,0 @@
package transcode
import "github.com/asticode/go-astiav"
const (
DefaultVideoPayloadType = 96
DefaultVideoFPS = int(25)
DefaultVideoTimeBase = int(9000)
DefaultVideoClockRate = int(90000)
DefaultVideoHeight = int(1080)
DefaultVideoWidth = int(1920)
DefaultVideoPixFormat = astiav.PixelFormatYuv420P
DefaultVideoEncoderCodec = astiav.CodecIDH264
)
const (
DefaultAudioPayloadType = 111
DefaultAudioSampleRate = int(48000)
DefaultAudioFrameSize = int(960)
DefaultAudioSampleFormat = astiav.SampleFormatS16
DefaultAudioEncoderCodec = astiav.CodecIDOpus
)
var (
DefaultAudioChannelLayout = astiav.ChannelLayoutStereo
)

View File

@@ -21,15 +21,23 @@ type Decoder struct {
} }
func CreateDecoder(ctx context.Context, demuxer *Demuxer, options ...DecoderOption) (*Decoder, error) { func CreateDecoder(ctx context.Context, demuxer *Demuxer, options ...DecoderOption) (*Decoder, error) {
decoder := &Decoder{ var (
err error
contextOption DecoderOption
decoder *Decoder
)
decoder = &Decoder{
demuxer: demuxer, demuxer: demuxer,
buffer: buffer.CreateChannelBuffer(ctx, DefaultVideoFPS*3, internal.CreateFramePool()),
ctx: ctx, ctx: ctx,
} }
var err error contextOption = withVideoSetDecoderContext(demuxer)
if decoder.decoderContext.MediaType() == astiav.MediaTypeAudio {
contextOption = withAudioSetDecoderContext(demuxer)
}
options = append([]DecoderOption{withVideoSetDecoderContext(demuxer.codecParameters, demuxer.stream, demuxer.formatContext)}, options...) options = append([]DecoderOption{contextOption}, options...)
for _, option := range options { for _, option := range options {
if err = option(decoder); err != nil { if err = option(decoder); err != nil {
@@ -37,6 +45,10 @@ func CreateDecoder(ctx context.Context, demuxer *Demuxer, options ...DecoderOpti
} }
} }
if decoder.buffer == nil {
decoder.buffer = buffer.CreateChannelBuffer(ctx, 256, internal.CreateFramePool())
}
if err = decoder.decoderContext.Open(decoder.codec, nil); err != nil { if err = decoder.decoderContext.Open(decoder.codec, nil); err != nil {
return nil, err return nil, err
} }
@@ -91,14 +103,14 @@ loop1:
} }
func (decoder *Decoder) pushFrame(frame *astiav.Frame) error { func (decoder *Decoder) pushFrame(frame *astiav.Frame) error {
ctx, cancel := context.WithTimeout(decoder.ctx, time.Second/time.Duration(DefaultVideoFPS)) ctx, cancel := context.WithTimeout(decoder.ctx, time.Second)
defer cancel() defer cancel()
return decoder.buffer.Push(ctx, frame) return decoder.buffer.Push(ctx, frame)
} }
func (decoder *Decoder) GetFrame() (*astiav.Frame, error) { func (decoder *Decoder) GetFrame() (*astiav.Frame, error) {
ctx, cancel := context.WithTimeout(decoder.ctx, time.Second/time.Duration(DefaultVideoFPS)) ctx, cancel := context.WithTimeout(decoder.ctx, time.Second)
defer cancel() defer cancel()
return decoder.buffer.Pop(ctx) return decoder.buffer.Pop(ctx)

View File

@@ -1,16 +1,21 @@
package transcode package transcode
import "github.com/asticode/go-astiav" import (
"github.com/asticode/go-astiav"
buffer "github.com/harshabose/tools/buffer/pkg"
"github.com/harshabose/simple_webrtc_comm/transcode/internal"
)
type DecoderOption = func(*Decoder) error type DecoderOption = func(*Decoder) error
func withVideoSetDecoderContext(codecParameters *astiav.CodecParameters, videoStream *astiav.Stream, formatContext *astiav.FormatContext) func(*Decoder) error { func withVideoSetDecoderContext(demuxer *Demuxer) func(*Decoder) error {
return func(decoder *Decoder) error { return func(decoder *Decoder) error {
var ( var (
err error err error
) )
if decoder.codec = astiav.FindDecoder(codecParameters.CodecID()); decoder.codec == nil { if decoder.codec = astiav.FindDecoder(demuxer.codecParameters.CodecID()); decoder.codec == nil {
return ErrorNoCodecFound return ErrorNoCodecFound
} }
@@ -18,23 +23,23 @@ func withVideoSetDecoderContext(codecParameters *astiav.CodecParameters, videoSt
return ErrorAllocateCodecContext return ErrorAllocateCodecContext
} }
if err = videoStream.CodecParameters().ToCodecContext(decoder.decoderContext); err != nil { if err = demuxer.stream.CodecParameters().ToCodecContext(decoder.decoderContext); err != nil {
return ErrorFillCodecContext return ErrorFillCodecContext
} }
decoder.decoderContext.SetFramerate(formatContext.GuessFrameRate(videoStream, nil)) decoder.decoderContext.SetFramerate(demuxer.formatContext.GuessFrameRate(demuxer.stream, nil))
decoder.decoderContext.SetTimeBase(videoStream.TimeBase()) decoder.decoderContext.SetTimeBase(demuxer.stream.TimeBase())
return nil return nil
} }
} }
func withAudioSetDecoderContext(codecParameters *astiav.CodecParameters, stream *astiav.Stream) func(*Decoder) error { func withAudioSetDecoderContext(demuxer *Demuxer) func(*Decoder) error {
return func(decoder *Decoder) error { return func(decoder *Decoder) error {
var ( var (
err error err error
) )
if decoder.codec = astiav.FindDecoder(codecParameters.CodecID()); decoder.codec == nil { if decoder.codec = astiav.FindDecoder(demuxer.codecParameters.CodecID()); decoder.codec == nil {
return ErrorNoCodecFound return ErrorNoCodecFound
} }
@@ -42,11 +47,18 @@ func withAudioSetDecoderContext(codecParameters *astiav.CodecParameters, stream
return ErrorAllocateCodecContext return ErrorAllocateCodecContext
} }
if err = stream.CodecParameters().ToCodecContext(decoder.decoderContext); err != nil { if err = demuxer.stream.CodecParameters().ToCodecContext(decoder.decoderContext); err != nil {
return ErrorFillCodecContext return ErrorFillCodecContext
} }
decoder.decoderContext.SetTimeBase(stream.TimeBase()) decoder.decoderContext.SetTimeBase(demuxer.stream.TimeBase())
return nil
}
}
func WithDecoderBufferSize(size int) DecoderOption {
return func(decoder *Decoder) error {
decoder.buffer = buffer.CreateChannelBuffer(decoder.ctx, size, internal.CreateFramePool())
return nil return nil
} }
} }

View File

@@ -25,7 +25,6 @@ func CreateDemuxer(ctx context.Context, containerAddress string, options ...Demu
demuxer := &Demuxer{ demuxer := &Demuxer{
formatContext: astiav.AllocFormatContext(), formatContext: astiav.AllocFormatContext(),
inputOptions: astiav.NewDictionary(), inputOptions: astiav.NewDictionary(),
buffer: buffer.CreateChannelBuffer(ctx, DefaultVideoFPS*3, internal.CreatePacketPool()),
ctx: ctx, ctx: ctx,
} }
@@ -59,6 +58,10 @@ func CreateDemuxer(ctx context.Context, containerAddress string, options ...Demu
} }
demuxer.codecParameters = demuxer.stream.CodecParameters() demuxer.codecParameters = demuxer.stream.CodecParameters()
if demuxer.buffer == nil {
demuxer.buffer = buffer.CreateChannelBuffer(ctx, 256, internal.CreatePacketPool())
}
return demuxer, nil return demuxer, nil
} }
@@ -103,7 +106,7 @@ loop1:
} }
func (demuxer *Demuxer) pushPacket(packet *astiav.Packet) error { func (demuxer *Demuxer) pushPacket(packet *astiav.Packet) error {
ctx, cancel := context.WithTimeout(demuxer.ctx, time.Second/time.Duration(DefaultVideoFPS)) // TODO: NEEDS TO BE BASED ON FPS ON INPUT_FORMAT ctx, cancel := context.WithTimeout(demuxer.ctx, time.Second) // TODO: NEEDS TO BE BASED ON FPS ON INPUT_FORMAT
defer cancel() defer cancel()
return demuxer.buffer.Push(ctx, packet) return demuxer.buffer.Push(ctx, packet)
@@ -114,7 +117,7 @@ func (demuxer *Demuxer) WaitForPacket() chan *astiav.Packet {
} }
func (demuxer *Demuxer) GetPacket() (*astiav.Packet, error) { func (demuxer *Demuxer) GetPacket() (*astiav.Packet, error) {
ctx, cancel := context.WithTimeout(demuxer.ctx, time.Second/time.Duration(DefaultVideoFPS)) ctx, cancel := context.WithTimeout(demuxer.ctx, time.Second)
defer cancel() defer cancel()
return demuxer.buffer.Pop(ctx) return demuxer.buffer.Pop(ctx)

View File

@@ -1,6 +1,11 @@
package transcode package transcode
import "github.com/asticode/go-astiav" import (
"github.com/asticode/go-astiav"
buffer "github.com/harshabose/tools/buffer/pkg"
"github.com/harshabose/simple_webrtc_comm/transcode/internal"
)
type DemuxerOption = func(*Demuxer) error type DemuxerOption = func(*Demuxer) error
@@ -35,3 +40,10 @@ func WithAvFoundationInputFormatOption(demuxer *Demuxer) error {
demuxer.inputFormat = astiav.FindInputFormat("avfoundation") demuxer.inputFormat = astiav.FindInputFormat("avfoundation")
return nil return nil
} }
func WithDemuxerBufferSize(size int) DemuxerOption {
return func(demuxer *Demuxer) error {
demuxer.buffer = buffer.CreateChannelBuffer(demuxer.ctx, size, internal.CreatePacketPool())
return nil
}
}

View File

@@ -27,7 +27,6 @@ type Encoder struct {
func CreateEncoder(ctx context.Context, codecID astiav.CodecID, filter *Filter, options ...EncoderOption) (*Encoder, error) { func CreateEncoder(ctx context.Context, codecID astiav.CodecID, filter *Filter, options ...EncoderOption) (*Encoder, error) {
encoder := &Encoder{ encoder := &Encoder{
buffer: buffer.CreateChannelBuffer(ctx, DefaultVideoFPS*3, internal.CreatePacketPool()),
filter: filter, filter: filter,
codecFlags: astiav.NewDictionary(), codecFlags: astiav.NewDictionary(),
ctx: ctx, ctx: ctx,
@@ -38,6 +37,13 @@ func CreateEncoder(ctx context.Context, codecID astiav.CodecID, filter *Filter,
return nil, ErrorAllocateCodecContext return nil, ErrorAllocateCodecContext
} }
contextOption := withAudioSetEncoderContextParameters(filter)
if filter.sinkContext.MediaType() == astiav.MediaTypeAudio {
contextOption = withVideoSetEncoderContextParameters(filter)
}
options = append([]EncoderOption{contextOption}, options...)
for _, option := range options { for _, option := range options {
if err := option(encoder); err != nil { if err := option(encoder); err != nil {
return nil, err return nil, err
@@ -54,7 +60,9 @@ func CreateEncoder(ctx context.Context, codecID astiav.CodecID, filter *Filter,
return nil, err return nil, err
} }
//encoder.findParameterSets(encoder.encoderContext.ExtraData()) if encoder.buffer == nil {
encoder.buffer = buffer.CreateChannelBuffer(ctx, 256, internal.CreatePacketPool())
}
return encoder, nil return encoder, nil
} }
@@ -63,12 +71,15 @@ func (encoder *Encoder) Start() {
go encoder.loop() go encoder.loop()
} }
func (encoder *Encoder) GetFPS() int { // TODO: THIS NEEDS TO BE ABSTRACTED func (encoder *Encoder) GetDuration() time.Duration {
return DefaultVideoFPS if encoder.encoderContext.MediaType() == astiav.MediaTypeAudio {
return time.Second * time.Duration(encoder.encoderContext.FrameSize()) / time.Duration(encoder.encoderContext.SampleRate())
}
return time.Second / time.Duration(encoder.encoderContext.Framerate().Float64())
} }
func (encoder *Encoder) GetVideoTimeBase() int { // TODO: THIS NEEDS TO BE ABSTRACTED func (encoder *Encoder) GetVideoTimeBase() astiav.Rational {
return DefaultVideoClockRate return encoder.encoderContext.TimeBase()
} }
func (encoder *Encoder) loop() { func (encoder *Encoder) loop() {
@@ -137,46 +148,6 @@ func (encoder *Encoder) SetBitrateChannel(channel chan int64) {
encoder.bandwidthChan = channel encoder.bandwidthChan = channel
} }
//func (encoder *Encoder) findParameterSets(extraData []byte) {
// if len(extraData) > 0 {
// // Find first start code (0x00000001)
// for i := 0; i < len(extraData)-4; i++ {
// if extraData[i] == 0 && extraData[i+1] == 0 && extraData[i+2] == 0 && extraData[i+3] == 1 {
// // Skip start code to get NAL type
// nalType := extraData[i+4] & 0x1F
//
// // Find next start code or end
// nextStart := len(extraData)
// for j := i + 4; j < len(extraData)-4; j++ {
// if extraData[j] == 0 && extraData[j+1] == 0 && extraData[j+2] == 0 && extraData[j+3] == 1 {
// nextStart = j
// break
// }
// }
//
// if nalType == 7 { // SPS
// encoder.sps = make([]byte, nextStart-i)
// copy(encoder.sps, extraData[i:nextStart])
// } else if nalType == 8 { // PPS
// encoder.pps = make([]byte, len(extraData)-i)
// copy(encoder.pps, extraData[i:])
// }
//
// i = nextStart - 1
// }
// }
// }
//
//}
//
//func (encoder *Encoder) GetSPS() []byte {
// return encoder.sps
//}
//
//func (encoder *Encoder) GetPPS() []byte {
// return encoder.pps
//}
func (encoder *Encoder) close() { func (encoder *Encoder) close() {
if encoder.encoderContext != nil { if encoder.encoderContext != nil {
encoder.encoderContext.Free() encoder.encoderContext.Free()

View File

@@ -1,6 +1,12 @@
package transcode package transcode
import "reflect" import (
"reflect"
buffer "github.com/harshabose/tools/buffer/pkg"
"github.com/harshabose/simple_webrtc_comm/transcode/internal"
)
type ( type (
EncoderOption = func(*Encoder) error EncoderOption = func(*Encoder) error
@@ -203,3 +209,32 @@ func WithX264LowBandwidthOptions(encoder *Encoder) error {
return encoder.codecFlags.Set(key, value, 0) return encoder.codecFlags.Set(key, value, 0)
}) })
} }
func withVideoSetEncoderContextParameters(filter *Filter) EncoderOption {
return func(encoder *Encoder) error {
encoder.encoderContext.SetHeight(filter.sinkContext.Height())
encoder.encoderContext.SetWidth(filter.sinkContext.Width())
encoder.encoderContext.SetTimeBase(filter.sinkContext.TimeBase())
encoder.encoderContext.SetPixelFormat(filter.sinkContext.PixelFormat())
encoder.encoderContext.SetFramerate(filter.sinkContext.FrameRate())
return nil
}
}
func withAudioSetEncoderContextParameters(filter *Filter) EncoderOption {
return func(encoder *Encoder) error {
encoder.encoderContext.SetTimeBase(filter.sinkContext.TimeBase())
encoder.encoderContext.SetSampleRate(filter.sinkContext.SampleRate())
encoder.encoderContext.SetSampleFormat(filter.sinkContext.SampleFormat())
encoder.encoderContext.SetChannelLayout(filter.sinkContext.ChannelLayout())
encoder.encoderContext.SetStrictStdCompliance(-2)
return nil
}
}
func WithEncoderBufferSize(size int) EncoderOption {
return func(encoder *Encoder) error {
encoder.buffer = buffer.CreateChannelBuffer(encoder.ctx, size, internal.CreatePacketPool())
return nil
}
}

View File

@@ -26,15 +26,15 @@ type Filter struct {
func CreateFilter(ctx context.Context, decoder *Decoder, filterConfig *FilterConfig, options ...FilterOption) (*Filter, error) { func CreateFilter(ctx context.Context, decoder *Decoder, filterConfig *FilterConfig, options ...FilterOption) (*Filter, error) {
var ( var (
filter *Filter filter *Filter
filterSrc *astiav.Filter filterSrc *astiav.Filter
filterSink *astiav.Filter filterSink *astiav.Filter
err error contextOption FilterOption
err error
) )
filter = &Filter{ filter = &Filter{
graph: astiav.AllocFilterGraph(), graph: astiav.AllocFilterGraph(),
decoder: decoder, decoder: decoder,
buffer: buffer.CreateChannelBuffer(ctx, DefaultVideoFPS*3, internal.CreateFramePool()),
input: astiav.AllocFilterInOut(), input: astiav.AllocFilterInOut(),
output: astiav.AllocFilterInOut(), output: astiav.AllocFilterInOut(),
srcContextParams: astiav.AllocBuffersrcFilterContextParameters(), srcContextParams: astiav.AllocBuffersrcFilterContextParameters(),
@@ -58,7 +58,12 @@ func CreateFilter(ctx context.Context, decoder *Decoder, filterConfig *FilterCon
return nil, ErrorAllocSinkContext return nil, ErrorAllocSinkContext
} }
options = append([]FilterOption{withVideoSetFilterContextParameters(decoder.decoderContext)}, options...) contextOption = withVideoSetFilterContextParameters(decoder)
if decoder.decoderContext.MediaType() == astiav.MediaTypeAudio {
contextOption = withAudioSetFilterContextParameters(decoder)
}
options = append([]FilterOption{contextOption}, options...)
for _, option := range options { for _, option := range options {
if err = option(filter); err != nil { if err = option(filter); err != nil {
@@ -67,6 +72,10 @@ func CreateFilter(ctx context.Context, decoder *Decoder, filterConfig *FilterCon
} }
} }
if filter.buffer == nil {
filter.buffer = buffer.CreateChannelBuffer(ctx, 256, internal.CreateFramePool())
}
if err = filter.srcContext.SetParameters(filter.srcContextParams); err != nil { if err = filter.srcContext.SetParameters(filter.srcContextParams); err != nil {
return nil, ErrorSrcContextSetParameter return nil, ErrorSrcContextSetParameter
} }

View File

@@ -4,6 +4,9 @@ import (
"fmt" "fmt"
"github.com/asticode/go-astiav" "github.com/asticode/go-astiav"
buffer "github.com/harshabose/tools/buffer/pkg"
"github.com/harshabose/simple_webrtc_comm/transcode/internal"
) )
type ( type (
@@ -38,143 +41,151 @@ var (
} }
) )
func withVideoSetFilterContextParameters(codecContext *astiav.CodecContext) func(*Filter) error { func WithFilterBufferSize(size int) FilterOption {
return func(filter *Filter) error { return func(filter *Filter) error {
filter.srcContextParams.SetHeight(codecContext.Height()) filter.buffer = buffer.CreateChannelBuffer(filter.ctx, size, internal.CreateFramePool())
filter.srcContextParams.SetPixelFormat(codecContext.PixelFormat())
filter.srcContextParams.SetSampleAspectRatio(codecContext.SampleAspectRatio())
filter.srcContextParams.SetTimeBase(codecContext.TimeBase())
filter.srcContextParams.SetWidth(codecContext.Width())
return nil return nil
} }
} }
func WithDefaultVideoFilterContentOptions(filter *Filter) error { func withVideoSetFilterContextParameters(decoder *Decoder) func(*Filter) error {
if err := videoScaleFilterContent(filter); err != nil { return func(filter *Filter) error {
return err filter.srcContextParams.SetHeight(decoder.decoderContext.Height())
} filter.srcContextParams.SetPixelFormat(decoder.decoderContext.PixelFormat())
if err := videoPixelFormatFilterContent(filter); err != nil { filter.srcContextParams.SetSampleAspectRatio(decoder.decoderContext.SampleAspectRatio())
return err filter.srcContextParams.SetTimeBase(decoder.decoderContext.TimeBase())
} filter.srcContextParams.SetWidth(decoder.decoderContext.Width())
if err := videoFPSFilterContent(filter); err != nil {
return err
}
return nil return nil
}
} }
func videoScaleFilterContent(filter *Filter) error { func WithVideoScaleFilterContent(width, height uint16) FilterOption {
filter.content += fmt.Sprintf("scale=%d:%d,", DefaultVideoWidth, DefaultVideoHeight) return func(filter *Filter) error {
return nil filter.content += fmt.Sprintf("scale=%d:%d,", width, height)
return nil
}
} }
func videoPixelFormatFilterContent(filter *Filter) error { func WithVideoPixelFormatFilterContent(pixelFormat astiav.PixelFormat) FilterOption {
filter.content += fmt.Sprintf("format=pix_fmts=%s,", DefaultVideoPixFormat) return func(filter *Filter) error {
return nil filter.content += fmt.Sprintf("format=pix_fmts=%s,", pixelFormat)
return nil
}
} }
func videoFPSFilterContent(filter *Filter) error { func WithVideoFPSFilterContent(fps uint8) FilterOption {
filter.content += fmt.Sprintf("fps=%d,", DefaultVideoFPS) return func(filter *Filter) error {
return nil filter.content += fmt.Sprintf("fps=%d,", fps)
return nil
}
} }
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
func withAudioSetFilterContextParameters(codecContext *astiav.CodecContext) func(*Filter) error { func withAudioSetFilterContextParameters(decoder *Decoder) func(*Filter) error {
return func(filter *Filter) error { return func(filter *Filter) error {
filter.srcContextParams.SetChannelLayout(codecContext.ChannelLayout()) filter.srcContextParams.SetChannelLayout(decoder.decoderContext.ChannelLayout())
filter.srcContextParams.SetSampleFormat(codecContext.SampleFormat()) filter.srcContextParams.SetSampleFormat(decoder.decoderContext.SampleFormat())
filter.srcContextParams.SetSampleRate(codecContext.SampleRate()) filter.srcContextParams.SetSampleRate(decoder.decoderContext.SampleRate())
filter.srcContextParams.SetTimeBase(codecContext.TimeBase()) filter.srcContextParams.SetTimeBase(decoder.decoderContext.TimeBase())
return nil return nil
} }
} }
func WithDefaultAudioFilterContentOptions(filter *Filter) error { func WithAudioSampleFormatChannelLayoutFilter(sampleFormat astiav.SampleFormat, channelLayout astiav.ChannelLayout) FilterOption {
if err := audioSampleFormatChannelLayoutContent(filter); err != nil { return func(filter *Filter) error {
return err filter.content += fmt.Sprintf("aformat=sample_fmts=%s:channel_layouts=%s", sampleFormat.String(), channelLayout.String()) + ","
return nil
} }
if err := audioSampleRateContent(filter); err != nil { }
return err
func WithAudioSampleRateFilter(samplerate uint32) FilterOption {
return func(filter *Filter) error {
filter.content += fmt.Sprintf("aresample=%d,", samplerate)
return nil
} }
if err := audioFrameSizeContent(filter); err != nil { }
return err
func WithAudioFrameSizeContent(framesize uint16) FilterOption {
return func(filter *Filter) error {
filter.content += fmt.Sprintf("asetnsamples=%d,", framesize)
return nil
} }
return nil
} }
func audioSampleFormatChannelLayoutContent(filter *Filter) error { func WithAudioCompressionContent(threshold int, ratio int, attack float64, release float64) FilterOption {
filter.content += buildAudioFormatContent(DefaultAudioSampleFormat, DefaultAudioChannelLayout) + "," return func(filter *Filter) error {
return nil // NOTE: DYNAMIC RANGE COMPRESSION TO HANDLE SUDDEN VOLUME CHANGES
// Possible values 'acompressor=threshold=-12dB:ratio=2:attack=0.05:release=0.2" // MOST POPULAR VALUES
filter.content += fmt.Sprintf("acompressor=threshold=%ddB:ratio=%d:attack=%.2f:release=%.2f,",
threshold, ratio, attack, release)
return nil
}
} }
func buildAudioFormatContent(sampleFormat astiav.SampleFormat, channelLayout astiav.ChannelLayout) string { func WithAudioHighPassContent(frequency int) FilterOption {
return fmt.Sprintf("aformat=sample_fmts=%s:channel_layouts=%s", sampleFormat.String(), channelLayout.String()) return func(filter *Filter) error {
// NOTE: HIGH-PASS FILTER TO REMOVE WIND NOISE AND TURBULENCE
// NOTE: 120HZ CUTOFF MIGHT PRESERVE VOICE WHILE REMOVING LOW RUMBLE; BUT MORE TESTING IS NEEDED
filter.content += fmt.Sprintf("highpass=f=%d,", frequency)
return nil
}
} }
func audioSampleRateContent(filter *Filter) error { func WithAudioNotchFilterContent() FilterOption {
filter.content += fmt.Sprintf("aresample=%d,", DefaultAudioSampleRate) return func(filter *Filter) error {
return nil // NOTE: NOTCH FILTER CAN BE USED TO TARGET SPECIFIC PROPELLER NOISE AND REMOVE THEM
} // NOTE: THIS MIGHT BE UNIQUE TO DRONE AND POWER LEVELS. I AM NOT SURE HOW TO USE IT TOO.
filter.content += "afftfilt=real='re*cos(0)':imag='im*cos(0):win_size=1024:fixed=true',"
func audioFrameSizeContent(filter *Filter) error { return nil
filter.content += fmt.Sprintf("asetnsamples=%d,", DefaultAudioFrameSize) }
return nil
}
func audioCompressionContent(filter *Filter) error {
// NOTE: DYNAMIC RANGE COMPRESSION TO HANDLE SUDDEN VOLUME CHANGES
// Possible values 'acompressor=threshold=-12dB:ratio=2:attack=0.05:release=0.2" // MOST POPULAR VALUES
filter.content += fmt.Sprintf("acompressor=threshold=%ddB:ratio=%d:attack=%d:release=%d,")
return nil
}
func audioHighPassContent(filter *Filter) error {
// NOTE: HIGH-PASS FILTER TO REMOVE WIND NOISE AND TURBULENCE
// NOTE: 120HZ CUTOFF MIGHT PRESERVE VOICE WHILE REMOVING LOW RUMBLE; BUT MORE TESTING IS NEEDED
filter.content += fmt.Sprintf("highpass=f=%d,")
return nil
}
func audioNotchFilterContent(filter *Filter) error {
// NOTE: NOTCH FILTER CAN BE USED TO TARGET SPECIFIC PROPELLER NOISE AND REMOVE THEM
// NOTE: THIS MIGHT BE UNIQUE TO DRONE AND POWER LEVELS. I AM NOT SURE HOW TO USE IT TOO.
filter.content += "afftfilt=real='re*cos(0)':imag='im*cos(0):win_size=1024:fixed=true',"
return nil
} }
// WARN: DO NOT USE FOR NOW // WARN: DO NOT USE FOR NOW
func audioNeuralNetworkDenoiserContent(filter *Filter) error { func WithAudioNeuralNetworkDenoiserContent(model string) FilterOption {
// NOTE: A RECURRENT NEURAL NETWORK MIGHT BE THE BEST SOLUTION HERE BUT I AM NOT SURE HOW TO BUILD IT return func(filter *Filter) error {
filter.content += "arnndn=m=," // NOTE: A RECURRENT NEURAL NETWORK MIGHT BE THE BEST SOLUTION HERE BUT I AM NOT SURE HOW TO BUILD IT
return nil filter.content += fmt.Sprintf("arnndn=m=%s,", model)
return nil
}
} }
func audioEqualiser(filter *Filter) error { func WithAudioEqualiserContent(frequency int, width int, gain int) FilterOption {
// NOTE: EQUALISER CAN BE USED TO ENHANCE SPEECH BANDWIDTH (300 - 3kHz). MORE RESEARCH NEEDS TO DONE return func(filter *Filter) error {
filter.content += fmt.Sprintf("equalizer=f=%d:t=h:width=%d:g=%d,") // NOTE: EQUALISER CAN BE USED TO ENHANCE SPEECH BANDWIDTH (300 - 3kHz). MORE RESEARCH NEEDS TO DONE
filter.content += fmt.Sprintf("equalizer=f=%d:t=h:width=%d:g=%d,",
return nil frequency, width, gain)
return nil
}
} }
func audioSilenceGateContent(filter *Filter) error { func WithAudioSilenceGateContent(threshold int, range_ int, attack float64, release float64) FilterOption {
// NOTE: IF EVERYTHING WORKS, WE SHOULD HAVE LIGHT NOISE WHICH CAN BE CONSIDERED AS SILENCE. THIS GATE REMOVES SILENCE return func(filter *Filter) error {
// NOTE: POSSIBLE VALUES 'agate=threshold=-30dB:range=-30dB:attack=0.01:release=0.1" // MOST POPULAR; MORE TESTING IS NEEDED // NOTE: IF EVERYTHING WORKS, WE SHOULD HAVE LIGHT NOISE WHICH CAN BE CONSIDERED AS SILENCE. THIS GATE REMOVES SILENCE
filter.content += fmt.Sprintf("agate=threshold=%ddB:range=%ddB:attack=%d:release=%d,") // NOTE: POSSIBLE VALUES 'agate=threshold=-30dB:range=-30dB:attack=0.01:release=0.1" // MOST POPULAR; MORE TESTING IS NEEDED
return nil filter.content += fmt.Sprintf("agate=threshold=%ddB:range=%ddB:attack=%.2f:release=%.2f,",
threshold, range_, attack, release)
return nil
}
} }
func audioLoudnessNormaliseContent(filter *Filter) error { func WithAudioLoudnessNormaliseContent(intensity int, truePeak float64, range_ int) FilterOption {
// NOTE: NORMALISES THE FINAL AUDIO. MUST BE CALLED AT THE END return func(filter *Filter) error {
// NOTE: POSSIBLE VALUES "loudnorm=I=-16:TP=-1.5:LRA=11" // MOST POPULAR // NOTE: NORMALISES THE FINAL AUDIO. MUST BE CALLED AT THE END
filter.content += fmt.Sprintf("loudnorm=I=%d:TP=%d:LRA=%d") // NOTE: POSSIBLE VALUES "loudnorm=I=-16:TP=-1.5:LRA=11" // MOST POPULAR
return nil filter.content += fmt.Sprintf("loudnorm=I=%d:TP=%.1f:LRA=%d",
intensity, truePeak, range_)
return nil
}
} }
// WARN: DO NOT USE FOR NOW // WARN: DO NOT USE FOR NOW
func audioNoiseReductionContent(filter *Filter) error { func WithAudioNoiseReductionContent(strength int) FilterOption {
// NOTE: anlmdn IS A NOISE REDUCTION FILTER. THIS MIGHT EFFECT THE QUALITY SIGNIFICANTLY - USE CAREFULLY return func(filter *Filter) error {
filter.content += fmt.Sprintf("anlmdn=s=%d,") // NOTE: anlmdn IS A NOISE REDUCTION FILTER. THIS MIGHT EFFECT THE QUALITY SIGNIFICANTLY - USE CAREFULLY
return nil filter.content += fmt.Sprintf("anlmdn=s=%d,", strength)
return nil
}
} }