mirror of
https://github.com/harshabose/transcode.git
synced 2025-10-09 13:40:05 +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) {
|
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)
|
||||||
|
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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)
|
||||||
|
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -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()
|
||||||
|
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -29,12 +29,12 @@ func CreateFilter(ctx context.Context, decoder *Decoder, filterConfig *FilterCon
|
|||||||
filter *Filter
|
filter *Filter
|
||||||
filterSrc *astiav.Filter
|
filterSrc *astiav.Filter
|
||||||
filterSink *astiav.Filter
|
filterSink *astiav.Filter
|
||||||
|
contextOption FilterOption
|
||||||
err error
|
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
|
||||||
}
|
}
|
||||||
|
@@ -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 {
|
|
||||||
filter.content += fmt.Sprintf("scale=%d:%d,", DefaultVideoWidth, DefaultVideoHeight)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func videoPixelFormatFilterContent(filter *Filter) error {
|
func WithVideoScaleFilterContent(width, height uint16) FilterOption {
|
||||||
filter.content += fmt.Sprintf("format=pix_fmts=%s,", DefaultVideoPixFormat)
|
return func(filter *Filter) error {
|
||||||
|
filter.content += fmt.Sprintf("scale=%d:%d,", width, height)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func videoFPSFilterContent(filter *Filter) error {
|
func WithVideoPixelFormatFilterContent(pixelFormat astiav.PixelFormat) FilterOption {
|
||||||
filter.content += fmt.Sprintf("fps=%d,", DefaultVideoFPS)
|
return func(filter *Filter) error {
|
||||||
|
filter.content += fmt.Sprintf("format=pix_fmts=%s,", pixelFormat)
|
||||||
return nil
|
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 {
|
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()) + ","
|
||||||
}
|
|
||||||
if err := audioSampleRateContent(filter); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := audioFrameSizeContent(filter); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func audioSampleFormatChannelLayoutContent(filter *Filter) error {
|
func WithAudioSampleRateFilter(samplerate uint32) FilterOption {
|
||||||
filter.content += buildAudioFormatContent(DefaultAudioSampleFormat, DefaultAudioChannelLayout) + ","
|
return func(filter *Filter) error {
|
||||||
|
filter.content += fmt.Sprintf("aresample=%d,", samplerate)
|
||||||
return nil
|
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 {
|
func WithAudioFrameSizeContent(framesize uint16) FilterOption {
|
||||||
filter.content += fmt.Sprintf("aresample=%d,", DefaultAudioSampleRate)
|
return func(filter *Filter) error {
|
||||||
|
filter.content += fmt.Sprintf("asetnsamples=%d,", framesize)
|
||||||
return nil
|
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
|
// 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
|
// 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
|
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: HIGH-PASS FILTER TO REMOVE WIND NOISE AND TURBULENCE
|
||||||
// NOTE: 120HZ CUTOFF MIGHT PRESERVE VOICE WHILE REMOVING LOW RUMBLE; BUT MORE TESTING IS NEEDED
|
// 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
|
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: 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.
|
// 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',"
|
filter.content += "afftfilt=real='re*cos(0)':imag='im*cos(0):win_size=1024:fixed=true',"
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// WARN: DO NOT USE FOR NOW
|
// 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
|
// 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
|
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
|
// 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
|
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: 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
|
// 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
|
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: NORMALISES THE FINAL AUDIO. MUST BE CALLED AT THE END
|
||||||
// NOTE: POSSIBLE VALUES "loudnorm=I=-16:TP=-1.5:LRA=11" // MOST POPULAR
|
// 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
|
return nil
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// WARN: DO NOT USE FOR NOW
|
// 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
|
// 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
|
return nil
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user