344 lines
8.3 KiB
Go
344 lines
8.3 KiB
Go
package ffmpeg
|
|
|
|
import (
|
|
/*
|
|
#include "ffmpeg.h"
|
|
int wrap_avcodec_decode_audio4(AVCodecContext *ctx, AVFrame *frame, void *data, int size, int *got) {
|
|
struct AVPacket pkt = {.data = data, .size = size};
|
|
return avcodec_decode_audio4(ctx, frame, got, &pkt);
|
|
}
|
|
void set_sample_fmt(AVCodecContext *ctx, int sample_fmt) {
|
|
ctx->sample_fmt = sample_fmt;
|
|
}
|
|
*/
|
|
"C"
|
|
"unsafe"
|
|
"runtime"
|
|
"fmt"
|
|
"github.com/nareix/av"
|
|
"github.com/nareix/codec/aacparser"
|
|
)
|
|
|
|
type ffctx struct {
|
|
ff C.FFCtx
|
|
}
|
|
|
|
type AudioEncoder struct {
|
|
ff *ffctx
|
|
SampleRate int
|
|
BitRate int
|
|
ChannelCount int
|
|
SampleFormat av.SampleFormat
|
|
FrameSampleCount int
|
|
codecData av.AudioCodecData
|
|
}
|
|
|
|
func convffSampleFormat(ffsamplefmt int32) (sampleFormat av.SampleFormat, err error) {
|
|
switch ffsamplefmt {
|
|
case C.AV_SAMPLE_FMT_U8: ///< unsigned 8 bits
|
|
sampleFormat = av.U8
|
|
case C.AV_SAMPLE_FMT_S16: ///< signed 16 bits
|
|
sampleFormat = av.S16
|
|
case C.AV_SAMPLE_FMT_S32: ///< signed 32 bits
|
|
sampleFormat = av.U32
|
|
case C.AV_SAMPLE_FMT_FLT: ///< float
|
|
sampleFormat = av.FLT
|
|
case C.AV_SAMPLE_FMT_DBL: ///< double
|
|
sampleFormat = av.DBL
|
|
case C.AV_SAMPLE_FMT_U8P: ///< unsigned 8 bits, planar
|
|
sampleFormat = av.U8P
|
|
case C.AV_SAMPLE_FMT_S16P: ///< signed 16 bits, planar
|
|
sampleFormat = av.S16P
|
|
case C.AV_SAMPLE_FMT_S32P: ///< signed 32 bits, planar
|
|
sampleFormat = av.S32P
|
|
case C.AV_SAMPLE_FMT_FLTP: ///< float, planar
|
|
sampleFormat = av.FLTP
|
|
case C.AV_SAMPLE_FMT_DBLP: ///< double, planar
|
|
sampleFormat = av.DBLP
|
|
default:
|
|
err = fmt.Errorf("ffsamplefmt=%d invalid", ffsamplefmt)
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
func (self *AudioEncoder) Setup() (err error) {
|
|
ff := &self.ff.ff
|
|
|
|
ff.frame = C.av_frame_alloc()
|
|
if self.SampleFormat == av.SampleFormat(0) {
|
|
self.SampleFormat = av.FLTP
|
|
}
|
|
if self.BitRate == 0 {
|
|
self.BitRate = 50000
|
|
}
|
|
if self.SampleRate == 0 {
|
|
self.SampleRate = 44100
|
|
}
|
|
if self.ChannelCount == 0 {
|
|
self.ChannelCount = 2
|
|
}
|
|
C.set_sample_fmt(ff.codecCtx, C.int(self.SampleFormat))
|
|
ff.codecCtx.sample_rate = C.int(self.SampleRate)
|
|
ff.codecCtx.bit_rate = C.int(self.BitRate)
|
|
ff.codecCtx.channels = C.int(self.ChannelCount)
|
|
ff.codecCtx.strict_std_compliance = C.FF_COMPLIANCE_EXPERIMENTAL
|
|
if C.avcodec_open2(ff.codecCtx, ff.codec, nil) != 0 {
|
|
err = fmt.Errorf("avcodec_open2 failed")
|
|
return
|
|
}
|
|
if self.SampleFormat, err = convffSampleFormat(ff.codecCtx.sample_fmt); err != nil {
|
|
return
|
|
}
|
|
self.ChannelCount = int(ff.codecCtx.channels)
|
|
self.FrameSampleCount = int(ff.codecCtx.frame_size)
|
|
|
|
extradata := C.GoBytes(unsafe.Pointer(ff.codecCtx.extradata), ff.codecCtx.extradata_size)
|
|
switch ff.codecCtx.codec_id {
|
|
case C.AV_CODEC_ID_AAC:
|
|
if self.codecData, err = aacparser.NewCodecDataFromMPEG4AudioConfigBytes(extradata); err != nil {
|
|
return
|
|
}
|
|
|
|
default:
|
|
self.codecData = AudioCodecData{
|
|
channelCount: self.ChannelCount,
|
|
sampleFormat: self.SampleFormat,
|
|
sampleRate: self.SampleRate,
|
|
codecId: ff.codecCtx.codec_id,
|
|
extradata: extradata,
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (self *AudioEncoder) CodecData() (codec av.AudioCodecData) {
|
|
return self.codecData
|
|
}
|
|
|
|
func (self *AudioEncoder) Encode(sample []byte, flush bool) (gotPkt bool, pkt []byte, err error) {
|
|
ff := &self.ff.ff
|
|
nbSamples := self.FrameSampleCount
|
|
channelCount := int(ff.codecCtx.channels)
|
|
sampleSize := int(C.av_get_bytes_per_sample(ff.codecCtx.sample_fmt))
|
|
expectedSize := nbSamples*sampleSize*channelCount
|
|
|
|
frame := ff.frame
|
|
if flush {
|
|
frame = nil
|
|
} else {
|
|
if len(sample) != expectedSize {
|
|
err = fmt.Errorf("len(sample) should be %d", expectedSize)
|
|
return
|
|
}
|
|
|
|
frame.nb_samples = C.int(nbSamples)
|
|
frame.format = C.int(ff.codecCtx.sample_fmt)
|
|
frame.channel_layout = ff.codecCtx.channel_layout
|
|
if C.av_sample_fmt_is_planar(ff.codecCtx.sample_fmt) != 0 {
|
|
for i := 0; i < self.ChannelCount; i++ {
|
|
frame.data[i] = (*C.uint8_t)(unsafe.Pointer(&sample[i*nbSamples*sampleSize]))
|
|
frame.linesize[i] = C.int(nbSamples*sampleSize)
|
|
}
|
|
} else {
|
|
frame.data[0] = (*C.uint8_t)(unsafe.Pointer(&sample[0]))
|
|
frame.linesize[0] = C.int(channelCount*nbSamples*sampleSize)
|
|
}
|
|
//frame.extended_data = &frame.data[0]
|
|
}
|
|
|
|
cpkt := C.AVPacket{}
|
|
cgotpkt := C.int(0)
|
|
cerr := C.avcodec_encode_audio2(ff.codecCtx, &cpkt, frame, &cgotpkt)
|
|
if cerr < C.int(0) {
|
|
err = fmt.Errorf("avcodec_encode_audio2 failed: %d", cerr)
|
|
return
|
|
}
|
|
|
|
if cgotpkt != 0 {
|
|
gotPkt = true
|
|
pkt = C.GoBytes(unsafe.Pointer(cpkt.data), cpkt.size)
|
|
C.av_free_packet(&cpkt)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (self *AudioEncoder) Close() {
|
|
freeFFCtx(self.ff)
|
|
}
|
|
|
|
type AudioDecoder struct {
|
|
ff *ffctx
|
|
ChannelCount int
|
|
SampleFormat av.SampleFormat
|
|
Extradata []byte
|
|
}
|
|
|
|
func (self *AudioDecoder) Setup() (err error) {
|
|
ff := &self.ff.ff
|
|
|
|
ff.frame = C.av_frame_alloc()
|
|
|
|
if len(self.Extradata) > 0 {
|
|
ff.codecCtx.extradata = (*C.uint8_t)(unsafe.Pointer(&self.Extradata[0]))
|
|
ff.codecCtx.extradata_size = C.int(len(self.Extradata))
|
|
}
|
|
|
|
ff.codecCtx.channels = C.int(self.ChannelCount)
|
|
if C.avcodec_open2(ff.codecCtx, ff.codec, nil) != 0 {
|
|
err = fmt.Errorf("avcodec_open2 failed")
|
|
return
|
|
}
|
|
if self.SampleFormat, err = convffSampleFormat(ff.codecCtx.sample_fmt); err != nil {
|
|
return
|
|
}
|
|
self.ChannelCount = int(ff.codecCtx.channels)
|
|
|
|
return
|
|
}
|
|
|
|
func (self *AudioDecoder) Decode(frame []byte) (gotPkt bool, pkt []byte, err error) {
|
|
ff := &self.ff.ff
|
|
|
|
cgotpkt := C.int(0)
|
|
cerr := C.wrap_avcodec_decode_audio4(ff.codecCtx, ff.frame, unsafe.Pointer(&frame[0]), C.int(len(frame)), &cgotpkt)
|
|
if cerr < C.int(0) {
|
|
err = fmt.Errorf("avcodec_decode_audio4 failed: %d", cerr)
|
|
return
|
|
}
|
|
|
|
if cgotpkt != C.int(0) {
|
|
gotPkt = true
|
|
//pkt = C.GoBytes(unsafe.Pointer(cpkt.data), cpkt.size)
|
|
size := C.av_samples_get_buffer_size(nil, ff.codecCtx.channels, ff.frame.nb_samples, ff.codecCtx.sample_fmt, C.int(1))
|
|
pkt = C.GoBytes(unsafe.Pointer(ff.frame.data[0]), size)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (self *AudioDecoder) Close() {
|
|
freeFFCtx(self.ff)
|
|
}
|
|
|
|
//func HasEncoder(name string) bool
|
|
//func HasDecoder(name string) bool
|
|
//func EncodersList() []string
|
|
//func DecodersList() []string
|
|
|
|
func newFFCtxByCodec(codec *C.AVCodec) (ff *ffctx, err error) {
|
|
if codec == nil {
|
|
err = fmt.Errorf("AVCodec not found")
|
|
return
|
|
}
|
|
ff = &ffctx{}
|
|
ff.ff.codec = codec
|
|
ff.ff.codecCtx = C.avcodec_alloc_context3(codec)
|
|
runtime.SetFinalizer(ff, freeFFCtx)
|
|
return
|
|
}
|
|
|
|
func freeFFCtx(self *ffctx) {
|
|
ff := &self.ff
|
|
if ff.frame != nil {
|
|
C.av_frame_free(&ff.frame)
|
|
ff.frame = nil
|
|
}
|
|
if ff.codecCtx != nil {
|
|
C.avcodec_close(ff.codecCtx)
|
|
C.av_free(ff.codecCtx)
|
|
ff.codecCtx = nil
|
|
}
|
|
}
|
|
|
|
func NewAudioEncoder(
|
|
name string,
|
|
sampleFormat av.SampleFormat, sampleRate int, channelCount int, bitRate int,
|
|
) (enc *AudioEncoder, err error) {
|
|
_enc := &AudioEncoder{}
|
|
if _enc.ff, err = newFFCtxByCodec(C.avcodec_find_encoder_by_name(C.CString(name))); err != nil {
|
|
return
|
|
}
|
|
_enc.SampleFormat = sampleFormat
|
|
_enc.SampleRate = sampleRate
|
|
_enc.ChannelCount = channelCount
|
|
_enc.BitRate = bitRate
|
|
if err = _enc.Setup(); err != nil {
|
|
return
|
|
}
|
|
enc = _enc
|
|
return
|
|
}
|
|
|
|
func NewAudioDecoder(codec av.AudioCodecData) (dec *AudioDecoder, err error) {
|
|
_dec := &AudioDecoder{}
|
|
var id uint32
|
|
|
|
switch codec.Type() {
|
|
case av.AAC:
|
|
if aaccodec, ok := codec.(av.AACCodecData); ok {
|
|
_dec.Extradata = aaccodec.MPEG4AudioConfigBytes()
|
|
id = C.AV_CODEC_ID_AAC
|
|
} else {
|
|
err = fmt.Errorf("aac CodecData must be av.AACCodecData")
|
|
return
|
|
}
|
|
|
|
default:
|
|
if ffcodec, ok := codec.(AudioCodecData); ok {
|
|
_dec.SampleFormat = ffcodec.sampleFormat
|
|
_dec.ChannelCount = ffcodec.channelCount
|
|
_dec.Extradata = ffcodec.extradata
|
|
id = ffcodec.codecId
|
|
} else {
|
|
err = fmt.Errorf("invalid CodecData for ffmpeg to decode")
|
|
return
|
|
}
|
|
}
|
|
|
|
if _dec.ff, err = newFFCtxByCodec(C.avcodec_find_decoder(id)); err != nil {
|
|
return
|
|
}
|
|
if err = _dec.Setup(); err != nil {
|
|
return
|
|
}
|
|
|
|
dec = _dec
|
|
return
|
|
}
|
|
|
|
type AudioCodecData struct {
|
|
codecId uint32
|
|
sampleFormat av.SampleFormat
|
|
channelCount int
|
|
sampleRate int
|
|
extradata []byte
|
|
}
|
|
|
|
func (self AudioCodecData) Type() int {
|
|
return int(self.codecId)
|
|
}
|
|
|
|
func (self AudioCodecData) IsAudio() bool {
|
|
return true
|
|
}
|
|
|
|
func (self AudioCodecData) IsVideo() bool {
|
|
return false
|
|
}
|
|
|
|
func (self AudioCodecData) SampleRate() int {
|
|
return self.sampleRate
|
|
}
|
|
|
|
func (self AudioCodecData) SampleFormat() av.SampleFormat {
|
|
return self.sampleFormat
|
|
}
|
|
|
|
func (self AudioCodecData) ChannelCount() int {
|
|
return self.channelCount
|
|
}
|
|
|