From 25b181318eabc7c2bae73f74b11ecfc0592cb258 Mon Sep 17 00:00:00 2001 From: harshabose Date: Tue, 25 Feb 2025 23:58:03 +0530 Subject: [PATCH] general commit 25-02-2024 23:57 --- pkg/constants.go | 26 ------ pkg/decoder.go | 24 +++-- pkg/decoder_options.go | 32 +++++-- pkg/demuxer.go | 9 +- pkg/demuxer_options.go | 14 ++- pkg/encoder.go | 63 ++++--------- pkg/encoder_options.go | 37 +++++++- pkg/filter.go | 21 +++-- pkg/filter_options.go | 203 ++++++++++++++++++++++------------------- 9 files changed, 234 insertions(+), 195 deletions(-) delete mode 100644 pkg/constants.go diff --git a/pkg/constants.go b/pkg/constants.go deleted file mode 100644 index 84e244d..0000000 --- a/pkg/constants.go +++ /dev/null @@ -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 -) diff --git a/pkg/decoder.go b/pkg/decoder.go index 8c12194..7c6e658 100644 --- a/pkg/decoder.go +++ b/pkg/decoder.go @@ -21,15 +21,23 @@ type Decoder struct { } func CreateDecoder(ctx context.Context, demuxer *Demuxer, options ...DecoderOption) (*Decoder, error) { - decoder := &Decoder{ + var ( + err error + contextOption DecoderOption + decoder *Decoder + ) + + decoder = &Decoder{ demuxer: demuxer, - buffer: buffer.CreateChannelBuffer(ctx, DefaultVideoFPS*3, internal.CreateFramePool()), 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 { 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 { return nil, err } @@ -91,14 +103,14 @@ loop1: } 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() return decoder.buffer.Push(ctx, frame) } 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() return decoder.buffer.Pop(ctx) diff --git a/pkg/decoder_options.go b/pkg/decoder_options.go index 9fe105f..6a99d46 100644 --- a/pkg/decoder_options.go +++ b/pkg/decoder_options.go @@ -1,16 +1,21 @@ 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 -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 { var ( 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 } @@ -18,23 +23,23 @@ func withVideoSetDecoderContext(codecParameters *astiav.CodecParameters, videoSt return ErrorAllocateCodecContext } - if err = videoStream.CodecParameters().ToCodecContext(decoder.decoderContext); err != nil { + if err = demuxer.stream.CodecParameters().ToCodecContext(decoder.decoderContext); err != nil { return ErrorFillCodecContext } - decoder.decoderContext.SetFramerate(formatContext.GuessFrameRate(videoStream, nil)) - decoder.decoderContext.SetTimeBase(videoStream.TimeBase()) + decoder.decoderContext.SetFramerate(demuxer.formatContext.GuessFrameRate(demuxer.stream, nil)) + decoder.decoderContext.SetTimeBase(demuxer.stream.TimeBase()) 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 { var ( 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 } @@ -42,11 +47,18 @@ func withAudioSetDecoderContext(codecParameters *astiav.CodecParameters, stream return ErrorAllocateCodecContext } - if err = stream.CodecParameters().ToCodecContext(decoder.decoderContext); err != nil { + if err = demuxer.stream.CodecParameters().ToCodecContext(decoder.decoderContext); err != nil { 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 } } diff --git a/pkg/demuxer.go b/pkg/demuxer.go index 3eb9cb9..07dee44 100644 --- a/pkg/demuxer.go +++ b/pkg/demuxer.go @@ -25,7 +25,6 @@ func CreateDemuxer(ctx context.Context, containerAddress string, options ...Demu demuxer := &Demuxer{ formatContext: astiav.AllocFormatContext(), inputOptions: astiav.NewDictionary(), - buffer: buffer.CreateChannelBuffer(ctx, DefaultVideoFPS*3, internal.CreatePacketPool()), ctx: ctx, } @@ -59,6 +58,10 @@ func CreateDemuxer(ctx context.Context, containerAddress string, options ...Demu } demuxer.codecParameters = demuxer.stream.CodecParameters() + if demuxer.buffer == nil { + demuxer.buffer = buffer.CreateChannelBuffer(ctx, 256, internal.CreatePacketPool()) + } + return demuxer, nil } @@ -103,7 +106,7 @@ loop1: } 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() return demuxer.buffer.Push(ctx, packet) @@ -114,7 +117,7 @@ func (demuxer *Demuxer) WaitForPacket() chan *astiav.Packet { } 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() return demuxer.buffer.Pop(ctx) diff --git a/pkg/demuxer_options.go b/pkg/demuxer_options.go index 88dcaa2..5ea6388 100644 --- a/pkg/demuxer_options.go +++ b/pkg/demuxer_options.go @@ -1,6 +1,11 @@ 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 @@ -35,3 +40,10 @@ func WithAvFoundationInputFormatOption(demuxer *Demuxer) error { demuxer.inputFormat = astiav.FindInputFormat("avfoundation") return nil } + +func WithDemuxerBufferSize(size int) DemuxerOption { + return func(demuxer *Demuxer) error { + demuxer.buffer = buffer.CreateChannelBuffer(demuxer.ctx, size, internal.CreatePacketPool()) + return nil + } +} diff --git a/pkg/encoder.go b/pkg/encoder.go index f556205..05b0c98 100644 --- a/pkg/encoder.go +++ b/pkg/encoder.go @@ -27,7 +27,6 @@ type Encoder struct { func CreateEncoder(ctx context.Context, codecID astiav.CodecID, filter *Filter, options ...EncoderOption) (*Encoder, error) { encoder := &Encoder{ - buffer: buffer.CreateChannelBuffer(ctx, DefaultVideoFPS*3, internal.CreatePacketPool()), filter: filter, codecFlags: astiav.NewDictionary(), ctx: ctx, @@ -38,6 +37,13 @@ func CreateEncoder(ctx context.Context, codecID astiav.CodecID, filter *Filter, return nil, ErrorAllocateCodecContext } + contextOption := withAudioSetEncoderContextParameters(filter) + if filter.sinkContext.MediaType() == astiav.MediaTypeAudio { + contextOption = withVideoSetEncoderContextParameters(filter) + } + + options = append([]EncoderOption{contextOption}, options...) + for _, option := range options { if err := option(encoder); err != nil { return nil, err @@ -54,7 +60,9 @@ func CreateEncoder(ctx context.Context, codecID astiav.CodecID, filter *Filter, return nil, err } - //encoder.findParameterSets(encoder.encoderContext.ExtraData()) + if encoder.buffer == nil { + encoder.buffer = buffer.CreateChannelBuffer(ctx, 256, internal.CreatePacketPool()) + } return encoder, nil } @@ -63,12 +71,15 @@ func (encoder *Encoder) Start() { go encoder.loop() } -func (encoder *Encoder) GetFPS() int { // TODO: THIS NEEDS TO BE ABSTRACTED - return DefaultVideoFPS +func (encoder *Encoder) GetDuration() time.Duration { + 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 - return DefaultVideoClockRate +func (encoder *Encoder) GetVideoTimeBase() astiav.Rational { + return encoder.encoderContext.TimeBase() } func (encoder *Encoder) loop() { @@ -137,46 +148,6 @@ func (encoder *Encoder) SetBitrateChannel(channel chan int64) { 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() { if encoder.encoderContext != nil { encoder.encoderContext.Free() diff --git a/pkg/encoder_options.go b/pkg/encoder_options.go index 80c2093..16e0843 100644 --- a/pkg/encoder_options.go +++ b/pkg/encoder_options.go @@ -1,6 +1,12 @@ package transcode -import "reflect" +import ( + "reflect" + + buffer "github.com/harshabose/tools/buffer/pkg" + + "github.com/harshabose/simple_webrtc_comm/transcode/internal" +) type ( EncoderOption = func(*Encoder) error @@ -203,3 +209,32 @@ func WithX264LowBandwidthOptions(encoder *Encoder) error { 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 + } +} diff --git a/pkg/filter.go b/pkg/filter.go index 65a2fcb..326d9f0 100644 --- a/pkg/filter.go +++ b/pkg/filter.go @@ -26,15 +26,15 @@ type Filter struct { func CreateFilter(ctx context.Context, decoder *Decoder, filterConfig *FilterConfig, options ...FilterOption) (*Filter, error) { var ( - filter *Filter - filterSrc *astiav.Filter - filterSink *astiav.Filter - err error + filter *Filter + filterSrc *astiav.Filter + filterSink *astiav.Filter + contextOption FilterOption + err error ) filter = &Filter{ graph: astiav.AllocFilterGraph(), decoder: decoder, - buffer: buffer.CreateChannelBuffer(ctx, DefaultVideoFPS*3, internal.CreateFramePool()), input: astiav.AllocFilterInOut(), output: astiav.AllocFilterInOut(), srcContextParams: astiav.AllocBuffersrcFilterContextParameters(), @@ -58,7 +58,12 @@ func CreateFilter(ctx context.Context, decoder *Decoder, filterConfig *FilterCon 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 { 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 { return nil, ErrorSrcContextSetParameter } diff --git a/pkg/filter_options.go b/pkg/filter_options.go index bc99c6a..1078f53 100644 --- a/pkg/filter_options.go +++ b/pkg/filter_options.go @@ -4,6 +4,9 @@ import ( "fmt" "github.com/asticode/go-astiav" + buffer "github.com/harshabose/tools/buffer/pkg" + + "github.com/harshabose/simple_webrtc_comm/transcode/internal" ) type ( @@ -38,143 +41,151 @@ var ( } ) -func withVideoSetFilterContextParameters(codecContext *astiav.CodecContext) func(*Filter) error { +func WithFilterBufferSize(size int) FilterOption { return func(filter *Filter) error { - filter.srcContextParams.SetHeight(codecContext.Height()) - filter.srcContextParams.SetPixelFormat(codecContext.PixelFormat()) - filter.srcContextParams.SetSampleAspectRatio(codecContext.SampleAspectRatio()) - filter.srcContextParams.SetTimeBase(codecContext.TimeBase()) - filter.srcContextParams.SetWidth(codecContext.Width()) + filter.buffer = buffer.CreateChannelBuffer(filter.ctx, size, internal.CreateFramePool()) return nil } } -func WithDefaultVideoFilterContentOptions(filter *Filter) error { - if err := videoScaleFilterContent(filter); err != nil { - return err - } - if err := videoPixelFormatFilterContent(filter); err != nil { - return err - } - if err := videoFPSFilterContent(filter); err != nil { - return err - } +func withVideoSetFilterContextParameters(decoder *Decoder) func(*Filter) error { + return func(filter *Filter) error { + filter.srcContextParams.SetHeight(decoder.decoderContext.Height()) + filter.srcContextParams.SetPixelFormat(decoder.decoderContext.PixelFormat()) + filter.srcContextParams.SetSampleAspectRatio(decoder.decoderContext.SampleAspectRatio()) + filter.srcContextParams.SetTimeBase(decoder.decoderContext.TimeBase()) + filter.srcContextParams.SetWidth(decoder.decoderContext.Width()) - return nil + return nil + } } -func videoScaleFilterContent(filter *Filter) error { - filter.content += fmt.Sprintf("scale=%d:%d,", DefaultVideoWidth, DefaultVideoHeight) - return nil +func WithVideoScaleFilterContent(width, height uint16) FilterOption { + return func(filter *Filter) error { + filter.content += fmt.Sprintf("scale=%d:%d,", width, height) + return nil + } } -func videoPixelFormatFilterContent(filter *Filter) error { - filter.content += fmt.Sprintf("format=pix_fmts=%s,", DefaultVideoPixFormat) - return nil +func WithVideoPixelFormatFilterContent(pixelFormat astiav.PixelFormat) FilterOption { + return func(filter *Filter) error { + filter.content += fmt.Sprintf("format=pix_fmts=%s,", pixelFormat) + return nil + } } -func videoFPSFilterContent(filter *Filter) error { - filter.content += fmt.Sprintf("fps=%d,", DefaultVideoFPS) - return nil +func WithVideoFPSFilterContent(fps uint8) FilterOption { + return func(filter *Filter) error { + 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 { - filter.srcContextParams.SetChannelLayout(codecContext.ChannelLayout()) - filter.srcContextParams.SetSampleFormat(codecContext.SampleFormat()) - filter.srcContextParams.SetSampleRate(codecContext.SampleRate()) - filter.srcContextParams.SetTimeBase(codecContext.TimeBase()) + filter.srcContextParams.SetChannelLayout(decoder.decoderContext.ChannelLayout()) + filter.srcContextParams.SetSampleFormat(decoder.decoderContext.SampleFormat()) + filter.srcContextParams.SetSampleRate(decoder.decoderContext.SampleRate()) + filter.srcContextParams.SetTimeBase(decoder.decoderContext.TimeBase()) return nil } } -func WithDefaultAudioFilterContentOptions(filter *Filter) error { - if err := audioSampleFormatChannelLayoutContent(filter); err != nil { - return err +func WithAudioSampleFormatChannelLayoutFilter(sampleFormat astiav.SampleFormat, channelLayout astiav.ChannelLayout) FilterOption { + return func(filter *Filter) error { + 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 { - filter.content += buildAudioFormatContent(DefaultAudioSampleFormat, DefaultAudioChannelLayout) + "," - return nil +func WithAudioCompressionContent(threshold int, ratio int, attack float64, release float64) FilterOption { + return func(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=%.2f:release=%.2f,", + threshold, ratio, attack, release) + return nil + } } -func buildAudioFormatContent(sampleFormat astiav.SampleFormat, channelLayout astiav.ChannelLayout) string { - return fmt.Sprintf("aformat=sample_fmts=%s:channel_layouts=%s", sampleFormat.String(), channelLayout.String()) +func WithAudioHighPassContent(frequency int) FilterOption { + 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 { - filter.content += fmt.Sprintf("aresample=%d,", DefaultAudioSampleRate) - return nil -} - -func audioFrameSizeContent(filter *Filter) error { - 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 +func WithAudioNotchFilterContent() FilterOption { + return func(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 -func audioNeuralNetworkDenoiserContent(filter *Filter) error { - // NOTE: A RECURRENT NEURAL NETWORK MIGHT BE THE BEST SOLUTION HERE BUT I AM NOT SURE HOW TO BUILD IT - filter.content += "arnndn=m=," - return nil +func WithAudioNeuralNetworkDenoiserContent(model string) FilterOption { + return func(filter *Filter) error { + // NOTE: A RECURRENT NEURAL NETWORK MIGHT BE THE BEST SOLUTION HERE BUT I AM NOT SURE HOW TO BUILD IT + filter.content += fmt.Sprintf("arnndn=m=%s,", model) + return nil + } } -func audioEqualiser(filter *Filter) error { - // 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 +func WithAudioEqualiserContent(frequency int, width int, gain int) FilterOption { + return func(filter *Filter) error { + // 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,", + frequency, width, gain) + return nil + } } -func audioSilenceGateContent(filter *Filter) error { - // NOTE: IF EVERYTHING WORKS, WE SHOULD HAVE LIGHT NOISE WHICH CAN BE CONSIDERED AS SILENCE. THIS GATE REMOVES SILENCE - // NOTE: POSSIBLE VALUES 'agate=threshold=-30dB:range=-30dB:attack=0.01:release=0.1" // MOST POPULAR; MORE TESTING IS NEEDED - filter.content += fmt.Sprintf("agate=threshold=%ddB:range=%ddB:attack=%d:release=%d,") - return nil +func WithAudioSilenceGateContent(threshold int, range_ int, attack float64, release float64) FilterOption { + return func(filter *Filter) error { + // NOTE: IF EVERYTHING WORKS, WE SHOULD HAVE LIGHT NOISE WHICH CAN BE CONSIDERED AS SILENCE. THIS GATE REMOVES SILENCE + // NOTE: POSSIBLE VALUES 'agate=threshold=-30dB:range=-30dB:attack=0.01:release=0.1" // MOST POPULAR; MORE TESTING IS NEEDED + filter.content += fmt.Sprintf("agate=threshold=%ddB:range=%ddB:attack=%.2f:release=%.2f,", + threshold, range_, attack, release) + return nil + } } -func audioLoudnessNormaliseContent(filter *Filter) error { - // NOTE: NORMALISES THE FINAL AUDIO. MUST BE CALLED AT THE END - // NOTE: POSSIBLE VALUES "loudnorm=I=-16:TP=-1.5:LRA=11" // MOST POPULAR - filter.content += fmt.Sprintf("loudnorm=I=%d:TP=%d:LRA=%d") - return nil +func WithAudioLoudnessNormaliseContent(intensity int, truePeak float64, range_ int) FilterOption { + return func(filter *Filter) error { + // NOTE: NORMALISES THE FINAL AUDIO. MUST BE CALLED AT THE END + // NOTE: POSSIBLE VALUES "loudnorm=I=-16:TP=-1.5:LRA=11" // MOST POPULAR + filter.content += fmt.Sprintf("loudnorm=I=%d:TP=%.1f:LRA=%d", + intensity, truePeak, range_) + return nil + } } // WARN: DO NOT USE FOR NOW -func audioNoiseReductionContent(filter *Filter) error { - // NOTE: anlmdn IS A NOISE REDUCTION FILTER. THIS MIGHT EFFECT THE QUALITY SIGNIFICANTLY - USE CAREFULLY - filter.content += fmt.Sprintf("anlmdn=s=%d,") - return nil +func WithAudioNoiseReductionContent(strength int) FilterOption { + return func(filter *Filter) error { + // NOTE: anlmdn IS A NOISE REDUCTION FILTER. THIS MIGHT EFFECT THE QUALITY SIGNIFICANTLY - USE CAREFULLY + filter.content += fmt.Sprintf("anlmdn=s=%d,", strength) + return nil + } }