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) {
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)

View File

@@ -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
}
}

View File

@@ -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)

View File

@@ -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
}
}

View File

@@ -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()

View File

@@ -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
}
}

View File

@@ -29,12 +29,12 @@ func CreateFilter(ctx context.Context, decoder *Decoder, filterConfig *FilterCon
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
}

View File

@@ -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
}
func videoScaleFilterContent(filter *Filter) error {
filter.content += fmt.Sprintf("scale=%d:%d,", DefaultVideoWidth, DefaultVideoHeight)
return nil
}
func videoPixelFormatFilterContent(filter *Filter) error {
filter.content += fmt.Sprintf("format=pix_fmts=%s,", DefaultVideoPixFormat)
func WithVideoScaleFilterContent(width, height uint16) FilterOption {
return func(filter *Filter) error {
filter.content += fmt.Sprintf("scale=%d:%d,", width, height)
return nil
}
}
func videoFPSFilterContent(filter *Filter) error {
filter.content += fmt.Sprintf("fps=%d,", DefaultVideoFPS)
func WithVideoPixelFormatFilterContent(pixelFormat astiav.PixelFormat) FilterOption {
return func(filter *Filter) error {
filter.content += fmt.Sprintf("format=pix_fmts=%s,", pixelFormat)
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
}
if err := audioSampleRateContent(filter); err != nil {
return err
}
if err := audioFrameSizeContent(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
}
}
func audioSampleFormatChannelLayoutContent(filter *Filter) error {
filter.content += buildAudioFormatContent(DefaultAudioSampleFormat, DefaultAudioChannelLayout) + ","
func WithAudioSampleRateFilter(samplerate uint32) FilterOption {
return func(filter *Filter) error {
filter.content += fmt.Sprintf("aresample=%d,", samplerate)
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 audioSampleRateContent(filter *Filter) error {
filter.content += fmt.Sprintf("aresample=%d,", DefaultAudioSampleRate)
func WithAudioFrameSizeContent(framesize uint16) FilterOption {
return func(filter *Filter) error {
filter.content += fmt.Sprintf("asetnsamples=%d,", framesize)
return nil
}
func audioFrameSizeContent(filter *Filter) error {
filter.content += fmt.Sprintf("asetnsamples=%d,", DefaultAudioFrameSize)
return nil
}
func audioCompressionContent(filter *Filter) error {
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=%d:release=%d,")
filter.content += fmt.Sprintf("acompressor=threshold=%ddB:ratio=%d:attack=%.2f:release=%.2f,",
threshold, ratio, attack, release)
return nil
}
}
func audioHighPassContent(filter *Filter) error {
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,")
filter.content += fmt.Sprintf("highpass=f=%d,", frequency)
return nil
}
}
func audioNotchFilterContent(filter *Filter) error {
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 {
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 += "arnndn=m=,"
filter.content += fmt.Sprintf("arnndn=m=%s,", model)
return nil
}
}
func audioEqualiser(filter *Filter) error {
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,")
filter.content += fmt.Sprintf("equalizer=f=%d:t=h:width=%d:g=%d,",
frequency, width, gain)
return nil
}
}
func audioSilenceGateContent(filter *Filter) error {
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=%d:release=%d,")
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 {
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=%d:LRA=%d")
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 {
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,")
filter.content += fmt.Sprintf("anlmdn=s=%d,", strength)
return nil
}
}