mirror of
https://github.com/harshabose/transcode.git
synced 2025-10-09 10:20:07 +08:00
general commit 25-02-2024 23:57
This commit is contained in:
@@ -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
|
||||
)
|
@@ -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)
|
||||
|
@@ -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
|
||||
}
|
||||
}
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
}
|
||||
}
|
||||
|
@@ -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()
|
||||
|
@@ -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
|
||||
}
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user