Last active
December 8, 2022 07:16
-
-
Save dbl0null/15dc217131e014a5deefcd7b3d68d94d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package transcoder | |
import ( | |
"errors" | |
"fmt" | |
"log" | |
"time" | |
"github.com/dbl0null/rt/internal/cgo/ffmpeg" | |
"github.com/deepch/vdk/av" | |
"github.com/deepch/vdk/av/pktque" | |
) | |
type AudioTranscoder struct { | |
Ready bool | |
prepared bool | |
timeline pktque.Timeline | |
encoder *ffmpeg.AudioEncoder | |
decoder *ffmpeg.AudioDecoder | |
EncoderData av.AudioCodecData | |
decoderData av.AudioCodecData | |
debug bool | |
} | |
// Default transcoder errors | |
var ( | |
ErrorTranscoderNotNeeded = errors.New("source audio codec already fit") | |
ErrorTranscoderUnsupported = errors.New("unsupported source audio codec") | |
ErrorTranscoderNoDecoder = errors.New("no audio decoder") | |
ErrorTranscoderNoEncoder = errors.New("no audio encoder") | |
ErrorTranscoderEncoderSetup = errors.New("audio encoder setup") | |
ErrorTranscoderEncoderCodecData = errors.New("audio encoder no codec data") | |
) | |
func (transcoder *AudioTranscoder) Setup(codecData av.CodecData) error { | |
switch codecData.Type() { | |
case av.PCM_ALAW, av.PCM_MULAW, av.PCM, av.AAC: | |
encoder, err := ffmpeg.NewAudioEncoderByCodecType(av.OPUS) | |
if err != nil { | |
log.Println(ErrorTranscoderNoEncoder, err) | |
return ErrorTranscoderNoEncoder | |
} | |
decoderCodecData := codecData.(av.AudioCodecData) | |
decoder, err := ffmpeg.NewAudioDecoder(decoderCodecData) | |
if err != nil { | |
transcoder.Close() | |
log.Println(ErrorTranscoderNoDecoder, err) | |
return ErrorTranscoderNoDecoder | |
} | |
_ = encoder.SetSampleRate(48000) | |
_ = encoder.SetChannelLayout(av.CH_STEREO) | |
_ = encoder.SetBitrate(48000) | |
if err = encoder.Setup(); err != nil { | |
transcoder.Close() | |
log.Println(ErrorTranscoderEncoderSetup, err) | |
return ErrorTranscoderEncoderSetup | |
} | |
transcoder.decoder = decoder | |
transcoder.encoder = encoder | |
transcoder.decoderData = decoderCodecData | |
transcoder.EncoderData, err = encoder.CodecData() | |
if err != nil { | |
transcoder.Close() | |
log.Println(ErrorTranscoderEncoderSetup, err) | |
return ErrorTranscoderEncoderCodecData | |
} | |
transcoder.Ready = true | |
case av.OPUS: | |
return ErrorTranscoderNotNeeded | |
default: | |
return ErrorTranscoderUnsupported | |
} | |
return nil | |
} | |
func MakeAudioTranscoder() AudioTranscoder { | |
return AudioTranscoder{ | |
Ready: false, | |
prepared: false, | |
debug: false, | |
timeline: pktque.Timeline{}, | |
} | |
} | |
func (transcoder *AudioTranscoder) Close() { | |
transcoder.Ready = false | |
if transcoder.encoder != nil { | |
transcoder.encoder.Close() | |
} | |
if transcoder.decoder != nil { | |
transcoder.decoder.Close() | |
} | |
} | |
func (transcoder *AudioTranscoder) Prepare(time time.Duration) { | |
// if !transcoder.prepared { | |
transcoder.timeline.Push(time, 100) | |
transcoder.prepared = true | |
// } | |
} | |
func (transcoder *AudioTranscoder) Do(packetAV *av.Packet) (outpkts []*av.Packet, err error) { | |
if !transcoder.Ready { | |
err = ErrorTranscoderNoDecoder | |
return | |
} | |
var dur time.Duration | |
var frame av.AudioFrame | |
var ok bool | |
if ok, frame, err = transcoder.decoder.Decode(packetAV.Data); err != nil { | |
return | |
} | |
if !ok { | |
return | |
} | |
if dur, err = transcoder.decoderData.PacketDuration(packetAV.Data); err != nil { | |
err = fmt.Errorf("transcode: PacketDuration() failed for input stream #%d", packetAV.Idx) | |
return | |
} | |
if transcoder.debug { | |
fmt.Println("transcode: push", packetAV.Time, dur) | |
} | |
transcoder.timeline.Push(packetAV.Time, dur) | |
var _outpkts [][]byte | |
if _outpkts, err = transcoder.encoder.Encode(frame); err != nil { | |
return | |
} | |
for _, _outpkt := range _outpkts { | |
if dur, err = transcoder.EncoderData.PacketDuration(_outpkt); err != nil { | |
err = fmt.Errorf("transcode: PacketDuration() failed for output stream #%d", packetAV.Idx) | |
return | |
} | |
outpkt := &av.Packet{Duration: dur, Idx: packetAV.Idx, Data: _outpkt} | |
outpkt.Time = transcoder.timeline.Pop(dur) | |
if transcoder.debug { | |
fmt.Println("transcode: pop", outpkt.Time, dur) | |
} | |
outpkts = append(outpkts, outpkt) | |
} | |
return | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment