From c0f9eb143278ba8693e1ebd4c7e84e4e2b85cdc1 Mon Sep 17 00:00:00 2001 From: nareix Date: Wed, 25 May 2016 07:42:08 +0800 Subject: [PATCH 01/17] first --- audio.go | 343 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ ffmpeg.go | 16 +++ ffmpeg.h | 12 ++ 3 files changed, 371 insertions(+) create mode 100644 audio.go create mode 100644 ffmpeg.go create mode 100644 ffmpeg.h diff --git a/audio.go b/audio.go new file mode 100644 index 0000000..60bea48 --- /dev/null +++ b/audio.go @@ -0,0 +1,343 @@ +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 +} + diff --git a/ffmpeg.go b/ffmpeg.go new file mode 100644 index 0000000..70f7cfb --- /dev/null +++ b/ffmpeg.go @@ -0,0 +1,16 @@ +package ffmpeg + +/* +#cgo LDFLAGS: -lavformat -lavutil -lavcodec +#include "ffmpeg.h" +void ffinit() { + av_register_all(); + av_log_set_level(AV_LOG_DEBUG); +} +*/ +import "C" + +func init() { + C.ffinit() +} + diff --git a/ffmpeg.h b/ffmpeg.h new file mode 100644 index 0000000..d039caa --- /dev/null +++ b/ffmpeg.h @@ -0,0 +1,12 @@ + +#include +#include +#include +#include + +typedef struct { + AVCodec *codec; + AVCodecContext *codecCtx; + AVFrame *frame; +} FFCtx; + From 45d195c6c12e2c40e3a63b797d011fce9b813574 Mon Sep 17 00:00:00 2001 From: nareix Date: Mon, 30 May 2016 08:18:23 +0800 Subject: [PATCH 02/17] add sample format / channel layout / AVFrame convention --- audio.go | 278 +++++++++++++++++++++++++++++++++++++++++------------- ffmpeg.go | 2 +- ffmpeg.h | 2 + 3 files changed, 214 insertions(+), 68 deletions(-) diff --git a/audio.go b/audio.go index 60bea48..b00c296 100644 --- a/audio.go +++ b/audio.go @@ -23,24 +23,87 @@ type ffctx struct { ff C.FFCtx } +type Resampler struct { + InSampleFormat, OutSampleFormat av.SampleFormat + InChannelLayout, OutChannelLayout av.ChannelLayout + InSampleRate, OutSampleRate int + avr *C.AVAudioResampleContext + inframe, outframe *C.AVFrame +} + +func freeResampler(self *Resampler) { + C.avresample_free(&self.avr) +} + +func (self *Resampler) Setup() (err error) { + avr := C.avresample_alloc_context() + C.av_opt_set_int(avr, C.CString("in_channel_layout"), C.int64_t(channelLayoutAV2FF(self.InChannelLayout)), 0) + C.av_opt_set_int(avr, C.CString("out_channel_layout"), C.int64_t(channelLayoutAV2FF(self.OutChannelLayout)), 0) + C.av_opt_set_int(avr, C.CString("in_sample_rate"), C.int64_t(self.InSampleRate), 0) + C.av_opt_set_int(avr, C.CString("out_sample_rate"), C.int64_t(self.OutSampleRate), 0) + C.av_opt_set_int(avr, C.CString("in_sample_fmt"), C.int64_t(sampleFormatAV2FF(self.InSampleFormat)), 0) + C.av_opt_set_int(avr, C.CString("out_sample_fmt"), C.int64_t(sampleFormatAV2FF(self.OutSampleFormat)), 0) + self.avr = avr + runtime.SetFinalizer(self, freeResampler) + if C.avresample_open(avr) != 0 { + err = fmt.Errorf("avresample_open failed") + return + } + self.inframe = C.av_frame_alloc() + self.outframe = C.av_frame_alloc() + return +} + +func (self *Resampler) Resample(in av.AudioFrame) (out av.AudioFrame) { + return +} + type AudioEncoder struct { ff *ffctx SampleRate int BitRate int - ChannelCount int + ChannelLayout av.ChannelLayout SampleFormat av.SampleFormat FrameSampleCount int + framebuf av.AudioFrame codecData av.AudioCodecData + resampler *Resampler } -func convffSampleFormat(ffsamplefmt int32) (sampleFormat av.SampleFormat, err error) { +func sampleFormatAV2FF(sampleFormat av.SampleFormat) (ffsamplefmt C.int) { + switch sampleFormat { + case av.U8: + ffsamplefmt = C.AV_SAMPLE_FMT_U8 + case av.S16: + ffsamplefmt = C.AV_SAMPLE_FMT_S16 + case av.S32: + ffsamplefmt = C.AV_SAMPLE_FMT_S32 + case av.FLT: + ffsamplefmt = C.AV_SAMPLE_FMT_FLT + case av.DBL: + ffsamplefmt = C.AV_SAMPLE_FMT_DBL + case av.U8P: + ffsamplefmt = C.AV_SAMPLE_FMT_U8P + case av.S16P: + ffsamplefmt = C.AV_SAMPLE_FMT_S16P + case av.S32P: + ffsamplefmt = C.AV_SAMPLE_FMT_S32P + case av.FLTP: + ffsamplefmt = C.AV_SAMPLE_FMT_FLTP + case av.DBLP: + ffsamplefmt = C.AV_SAMPLE_FMT_DBLP + } + return +} + +func sampleFormatFF2AV(ffsamplefmt int32) (sampleFormat av.SampleFormat) { 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 + sampleFormat = av.S32 case C.AV_SAMPLE_FMT_FLT: ///< float sampleFormat = av.FLT case C.AV_SAMPLE_FMT_DBL: ///< double @@ -55,9 +118,6 @@ func convffSampleFormat(ffsamplefmt int32) (sampleFormat av.SampleFormat, err er sampleFormat = av.FLTP case C.AV_SAMPLE_FMT_DBLP: ///< double, planar sampleFormat = av.DBLP - default: - err = fmt.Errorf("ffsamplefmt=%d invalid", ffsamplefmt) - return } return } @@ -75,22 +135,20 @@ func (self *AudioEncoder) Setup() (err error) { if self.SampleRate == 0 { self.SampleRate = 44100 } - if self.ChannelCount == 0 { - self.ChannelCount = 2 + if self.ChannelLayout == av.ChannelLayout(0) { + self.ChannelLayout = av.CH_STEREO } + 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.channel_layout = channelLayoutAV2FF(self.ChannelLayout) 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.SampleFormat = sampleFormatFF2AV(ff.codecCtx.sample_fmt) self.FrameSampleCount = int(ff.codecCtx.frame_size) extradata := C.GoBytes(unsafe.Pointer(ff.codecCtx.extradata), ff.codecCtx.extradata_size) @@ -102,7 +160,7 @@ func (self *AudioEncoder) Setup() (err error) { default: self.codecData = AudioCodecData{ - channelCount: self.ChannelCount, + channelLayout: self.ChannelLayout, sampleFormat: self.SampleFormat, sampleRate: self.SampleRate, codecId: ff.codecCtx.codec_id, @@ -117,40 +175,22 @@ func (self *AudioEncoder) CodecData() (codec av.AudioCodecData) { return self.codecData } -func (self *AudioEncoder) Encode(sample []byte, flush bool) (gotPkt bool, pkt []byte, err error) { +func (self *AudioEncoder) Encode(frame av.AudioFrame) (gotPkt bool, pkt av.Packet, 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) + if self.FrameSampleCount != 0 { + self.framebuf = self.framebuf.Concat(frame) + if self.framebuf.SampleCount < self.FrameSampleCount { 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] + frame = self.framebuf.Slice(0, self.FrameSampleCount) + self.framebuf = self.framebuf.Slice(self.FrameSampleCount, self.framebuf.SampleCount) } cpkt := C.AVPacket{} cgotpkt := C.int(0) - cerr := C.avcodec_encode_audio2(ff.codecCtx, &cpkt, frame, &cgotpkt) + audioFrameAssignToFF(frame, ff.frame) + cerr := C.avcodec_encode_audio2(ff.codecCtx, &cpkt, ff.frame, &cgotpkt) if cerr < C.int(0) { err = fmt.Errorf("avcodec_encode_audio2 failed: %d", cerr) return @@ -158,7 +198,8 @@ func (self *AudioEncoder) Encode(sample []byte, flush bool) (gotPkt bool, pkt [] if cgotpkt != 0 { gotPkt = true - pkt = C.GoBytes(unsafe.Pointer(cpkt.data), cpkt.size) + pkt.Data = C.GoBytes(unsafe.Pointer(cpkt.data), cpkt.size) + pkt.Duration = float64(frame.SampleCount)/float64(self.SampleRate) C.av_free_packet(&cpkt) } @@ -169,9 +210,92 @@ func (self *AudioEncoder) Close() { freeFFCtx(self.ff) } +func audioFrameAssignToAV(f *C.AVFrame, frame *av.AudioFrame) { + frame.SampleCount = int(f.nb_samples) + frame.SampleFormat = sampleFormatFF2AV(int32(f.format)) + frame.ChannelLayout = channelLayoutFF2AV(f.channel_layout) + channels := int(f.channels) + frame.Data = make([][]byte, channels) + for i := 0; i < channels; i++ { + frame.Data[i] = C.GoBytes(unsafe.Pointer(f.data[i]), f.linesize[i]) + } +} + +func audioFrameAssignToFF(frame av.AudioFrame, f *C.AVFrame) { + f.nb_samples = C.int(frame.SampleCount) + f.format = C.int(sampleFormatAV2FF(frame.SampleFormat)) + f.channel_layout = channelLayoutAV2FF(frame.ChannelLayout) + for i := range frame.Data { + f.data[i] = (*C.uint8_t)(unsafe.Pointer(&frame.Data[i])) + f.linesize[i] = C.int(len(frame.Data[i])) + } +} + +func channelLayoutFF2AV(layout C.uint64_t) (channelLayout av.ChannelLayout) { + if layout & C.AV_CH_FRONT_CENTER != 0 { + channelLayout |= av.CH_FRONT_CENTER + } + if layout & C.AV_CH_FRONT_LEFT != 0 { + channelLayout |= av.CH_FRONT_LEFT + } + if layout & C.AV_CH_FRONT_RIGHT != 0 { + channelLayout |= av.CH_FRONT_RIGHT + } + if layout & C.AV_CH_BACK_CENTER != 0 { + channelLayout |= av.CH_BACK_CENTER + } + if layout & C.AV_CH_BACK_LEFT != 0 { + channelLayout |= av.CH_BACK_LEFT + } + if layout & C.AV_CH_BACK_RIGHT != 0 { + channelLayout |= av.CH_BACK_RIGHT + } + if layout & C.AV_CH_SIDE_LEFT != 0 { + channelLayout |= av.CH_SIDE_LEFT + } + if layout & C.AV_CH_SIDE_RIGHT != 0 { + channelLayout |= av.CH_SIDE_RIGHT + } + if layout & C.AV_CH_LOW_FREQUENCY != 0 { + channelLayout |= av.CH_LOW_FREQ + } + return +} + +func channelLayoutAV2FF(channelLayout av.ChannelLayout) (layout C.uint64_t) { + if channelLayout & av.CH_FRONT_CENTER != 0 { + layout |= C.AV_CH_FRONT_CENTER + } + if channelLayout & av.CH_FRONT_LEFT != 0 { + layout |= C.AV_CH_FRONT_LEFT + } + if channelLayout & av.CH_FRONT_RIGHT != 0 { + layout |= C.AV_CH_FRONT_RIGHT + } + if channelLayout & av.CH_BACK_CENTER != 0 { + layout |= C.AV_CH_BACK_CENTER + } + if channelLayout & av.CH_BACK_LEFT != 0 { + layout |= C.AV_CH_BACK_LEFT + } + if channelLayout & av.CH_BACK_RIGHT != 0 { + layout |= C.AV_CH_BACK_RIGHT + } + if channelLayout & av.CH_SIDE_LEFT != 0 { + layout |= C.AV_CH_SIDE_LEFT + } + if channelLayout & av.CH_SIDE_RIGHT != 0 { + layout |= C.AV_CH_SIDE_RIGHT + } + if channelLayout & av.CH_LOW_FREQ != 0 { + layout |= C.AV_CH_LOW_FREQUENCY + } + return +} + type AudioDecoder struct { ff *ffctx - ChannelCount int + ChannelLayout av.ChannelLayout SampleFormat av.SampleFormat Extradata []byte } @@ -186,34 +310,30 @@ func (self *AudioDecoder) Setup() (err error) { ff.codecCtx.extradata_size = C.int(len(self.Extradata)) } - ff.codecCtx.channels = C.int(self.ChannelCount) + ff.codecCtx.channel_layout = channelLayoutAV2FF(self.ChannelLayout) 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.SampleFormat = sampleFormatFF2AV(ff.codecCtx.sample_fmt) + self.ChannelLayout = channelLayoutFF2AV(ff.codecCtx.channel_layout) return } -func (self *AudioDecoder) Decode(frame []byte) (gotPkt bool, pkt []byte, err error) { +func (self *AudioDecoder) Decode(data []byte) (gotFrame bool, frame av.AudioFrame, 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) + cerr := C.wrap_avcodec_decode_audio4(ff.codecCtx, ff.frame, unsafe.Pointer(&data[0]), C.int(len(data)), &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) + gotFrame = true + audioFrameAssignToAV(ff.frame, &frame) } return @@ -223,16 +343,18 @@ func (self *AudioDecoder) Close() { freeFFCtx(self.ff) } -//func HasEncoder(name string) bool -//func HasDecoder(name string) bool +func HasEncoder(name string) bool { + return C.avcodec_find_encoder_by_name(C.CString(name)) != nil +} + +func HasDecoder(name string) bool { + return C.avcodec_find_decoder_by_name(C.CString(name)) != nil +} + //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) @@ -255,15 +377,26 @@ func freeFFCtx(self *ffctx) { func NewAudioEncoder( name string, - sampleFormat av.SampleFormat, sampleRate int, channelCount int, bitRate int, + sampleFormat av.SampleFormat, sampleRate int, channelLayout av.ChannelLayout, bitRate int, ) (enc *AudioEncoder, err error) { _enc := &AudioEncoder{} - if _enc.ff, err = newFFCtxByCodec(C.avcodec_find_encoder_by_name(C.CString(name))); err != nil { + + codec := C.avcodec_find_encoder_by_name(C.CString(name)) + if codec == nil { + err = fmt.Errorf("cannot find encoder=%s", name) + return + } + if C.avcodec_get_type(codec.id) != C.AVMEDIA_TYPE_AUDIO { + err = fmt.Errorf("encoder=%s type is not audio", name) + return + } + + if _enc.ff, err = newFFCtxByCodec(codec); err != nil { return } _enc.SampleFormat = sampleFormat _enc.SampleRate = sampleRate - _enc.ChannelCount = channelCount + _enc.ChannelLayout = channelLayout _enc.BitRate = bitRate if err = _enc.Setup(); err != nil { return @@ -289,7 +422,7 @@ func NewAudioDecoder(codec av.AudioCodecData) (dec *AudioDecoder, err error) { default: if ffcodec, ok := codec.(AudioCodecData); ok { _dec.SampleFormat = ffcodec.sampleFormat - _dec.ChannelCount = ffcodec.channelCount + _dec.ChannelLayout = ffcodec.channelLayout _dec.Extradata = ffcodec.extradata id = ffcodec.codecId } else { @@ -298,7 +431,18 @@ func NewAudioDecoder(codec av.AudioCodecData) (dec *AudioDecoder, err error) { } } - if _dec.ff, err = newFFCtxByCodec(C.avcodec_find_decoder(id)); err != nil { + c := C.avcodec_find_decoder(id) + if c == nil { + err = fmt.Errorf("cannot find decoder id=%d", id) + return + } + + if C.avcodec_get_type(c.id) != C.AVMEDIA_TYPE_AUDIO { + err = fmt.Errorf("decoder id=%d type is not audio", c.id) + return + } + + if _dec.ff, err = newFFCtxByCodec(c); err != nil { return } if err = _dec.Setup(); err != nil { @@ -312,7 +456,7 @@ func NewAudioDecoder(codec av.AudioCodecData) (dec *AudioDecoder, err error) { type AudioCodecData struct { codecId uint32 sampleFormat av.SampleFormat - channelCount int + channelLayout av.ChannelLayout sampleRate int extradata []byte } @@ -337,7 +481,7 @@ func (self AudioCodecData) SampleFormat() av.SampleFormat { return self.sampleFormat } -func (self AudioCodecData) ChannelCount() int { - return self.channelCount +func (self AudioCodecData) ChannelLayout() av.ChannelLayout { + return self.channelLayout } diff --git a/ffmpeg.go b/ffmpeg.go index 70f7cfb..b9b5468 100644 --- a/ffmpeg.go +++ b/ffmpeg.go @@ -1,7 +1,7 @@ package ffmpeg /* -#cgo LDFLAGS: -lavformat -lavutil -lavcodec +#cgo LDFLAGS: -lavformat -lavutil -lavcodec -lavresample #include "ffmpeg.h" void ffinit() { av_register_all(); diff --git a/ffmpeg.h b/ffmpeg.h index d039caa..308fc52 100644 --- a/ffmpeg.h +++ b/ffmpeg.h @@ -2,6 +2,8 @@ #include #include #include +#include +#include #include typedef struct { From 1f907b025c45e3c7f0df1ae1aff8f6f26a28578b Mon Sep 17 00:00:00 2001 From: nareix Date: Mon, 30 May 2016 11:13:31 +0800 Subject: [PATCH 03/17] fix Encode() and audioFrameAssignToFF() --- audio.go | 116 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 71 insertions(+), 45 deletions(-) diff --git a/audio.go b/audio.go index b00c296..3fe4e67 100644 --- a/audio.go +++ b/audio.go @@ -7,9 +7,6 @@ import ( 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" @@ -24,37 +21,51 @@ type ffctx struct { } type Resampler struct { - InSampleFormat, OutSampleFormat av.SampleFormat - InChannelLayout, OutChannelLayout av.ChannelLayout - InSampleRate, OutSampleRate int + inSampleFormat, OutSampleFormat av.SampleFormat + inChannelLayout, OutChannelLayout av.ChannelLayout + inSampleRate, OutSampleRate int avr *C.AVAudioResampleContext inframe, outframe *C.AVFrame } func freeResampler(self *Resampler) { - C.avresample_free(&self.avr) + if self.avr != nil { + C.avresample_free(&self.avr) + self.avr = nil + } } -func (self *Resampler) Setup() (err error) { +func (self *Resampler) setupAvr() { avr := C.avresample_alloc_context() - C.av_opt_set_int(avr, C.CString("in_channel_layout"), C.int64_t(channelLayoutAV2FF(self.InChannelLayout)), 0) + C.av_opt_set_int(avr, C.CString("in_channel_layout"), C.int64_t(channelLayoutAV2FF(self.inChannelLayout)), 0) C.av_opt_set_int(avr, C.CString("out_channel_layout"), C.int64_t(channelLayoutAV2FF(self.OutChannelLayout)), 0) - C.av_opt_set_int(avr, C.CString("in_sample_rate"), C.int64_t(self.InSampleRate), 0) + C.av_opt_set_int(avr, C.CString("in_sample_rate"), C.int64_t(self.inSampleRate), 0) C.av_opt_set_int(avr, C.CString("out_sample_rate"), C.int64_t(self.OutSampleRate), 0) - C.av_opt_set_int(avr, C.CString("in_sample_fmt"), C.int64_t(sampleFormatAV2FF(self.InSampleFormat)), 0) + C.av_opt_set_int(avr, C.CString("in_sample_fmt"), C.int64_t(sampleFormatAV2FF(self.inSampleFormat)), 0) C.av_opt_set_int(avr, C.CString("out_sample_fmt"), C.int64_t(sampleFormatAV2FF(self.OutSampleFormat)), 0) self.avr = avr - runtime.SetFinalizer(self, freeResampler) - if C.avresample_open(avr) != 0 { - err = fmt.Errorf("avresample_open failed") - return - } - self.inframe = C.av_frame_alloc() - self.outframe = C.av_frame_alloc() - return } -func (self *Resampler) Resample(in av.AudioFrame) (out av.AudioFrame) { +func (self *Resampler) Resample(in av.AudioFrame) (out av.AudioFrame, err error) { + if self.inframe == nil { + self.inframe = C.av_frame_alloc() + self.outframe = C.av_frame_alloc() + runtime.SetFinalizer(self, freeResampler) + } + if in.SampleRate != self.inSampleRate || in.SampleFormat != self.inSampleFormat || in.ChannelLayout != self.inChannelLayout { + // TODO: flush left bytes + freeResampler(self) + self.inSampleFormat = in.SampleFormat + self.inSampleRate = in.SampleRate + self.inChannelLayout = in.ChannelLayout + self.setupAvr() + } + audioFrameAssignToFF(in, self.inframe) + if C.avresample_convert_frame(self.avr, self.outframe, self.inframe) != 0 { + err = fmt.Errorf("avresample_convert_frame failed") + return + } + audioFrameAssignToAV(self.outframe, &out) return } @@ -70,7 +81,7 @@ type AudioEncoder struct { resampler *Resampler } -func sampleFormatAV2FF(sampleFormat av.SampleFormat) (ffsamplefmt C.int) { +func sampleFormatAV2FF(sampleFormat av.SampleFormat) (ffsamplefmt int32) { switch sampleFormat { case av.U8: ffsamplefmt = C.AV_SAMPLE_FMT_U8 @@ -139,7 +150,8 @@ func (self *AudioEncoder) Setup() (err error) { self.ChannelLayout = av.CH_STEREO } - C.set_sample_fmt(ff.codecCtx, C.int(self.SampleFormat)) + //C.set_sample_fmt(ff.codecCtx, C.int(self.SampleFormat)) + ff.codecCtx.sample_fmt = sampleFormatAV2FF(self.SampleFormat) ff.codecCtx.sample_rate = C.int(self.SampleRate) ff.codecCtx.bit_rate = C.int(self.BitRate) ff.codecCtx.channel_layout = channelLayoutAV2FF(self.ChannelLayout) @@ -175,21 +187,13 @@ func (self *AudioEncoder) CodecData() (codec av.AudioCodecData) { return self.codecData } -func (self *AudioEncoder) Encode(frame av.AudioFrame) (gotPkt bool, pkt av.Packet, err error) { +func (self *AudioEncoder) encodeOne(frame av.AudioFrame) (gotpkt bool, pkt av.Packet, err error) { ff := &self.ff.ff - if self.FrameSampleCount != 0 { - self.framebuf = self.framebuf.Concat(frame) - if self.framebuf.SampleCount < self.FrameSampleCount { - return - } - frame = self.framebuf.Slice(0, self.FrameSampleCount) - self.framebuf = self.framebuf.Slice(self.FrameSampleCount, self.framebuf.SampleCount) - } - cpkt := C.AVPacket{} cgotpkt := C.int(0) audioFrameAssignToFF(frame, ff.frame) + cerr := C.avcodec_encode_audio2(ff.codecCtx, &cpkt, ff.frame, &cgotpkt) if cerr < C.int(0) { err = fmt.Errorf("avcodec_encode_audio2 failed: %d", cerr) @@ -197,7 +201,7 @@ func (self *AudioEncoder) Encode(frame av.AudioFrame) (gotPkt bool, pkt av.Packe } if cgotpkt != 0 { - gotPkt = true + gotpkt = true pkt.Data = C.GoBytes(unsafe.Pointer(cpkt.data), cpkt.size) pkt.Duration = float64(frame.SampleCount)/float64(self.SampleRate) C.av_free_packet(&cpkt) @@ -206,6 +210,38 @@ func (self *AudioEncoder) Encode(frame av.AudioFrame) (gotPkt bool, pkt av.Packe return } +func (self *AudioEncoder) Encode(frame av.AudioFrame) (pkts []av.Packet, err error) { + var gotpkt bool + var pkt av.Packet + + if self.FrameSampleCount != 0 { + if self.framebuf.SampleCount == 0 { + self.framebuf = frame + } else { + self.framebuf = self.framebuf.Concat(frame) + } + for self.framebuf.SampleCount >= self.FrameSampleCount { + frame := self.framebuf.Slice(0, self.FrameSampleCount) + if gotpkt, pkt, err = self.encodeOne(frame); err != nil { + return + } + if gotpkt { + pkts = append(pkts, pkt) + } + self.framebuf = self.framebuf.Slice(self.FrameSampleCount, self.framebuf.SampleCount) + } + } else { + if gotpkt, pkt, err = self.encodeOne(frame); err != nil { + return + } + if gotpkt { + pkts = append(pkts, pkt) + } + } + + return +} + func (self *AudioEncoder) Close() { freeFFCtx(self.ff) } @@ -226,7 +262,7 @@ func audioFrameAssignToFF(frame av.AudioFrame, f *C.AVFrame) { f.format = C.int(sampleFormatAV2FF(frame.SampleFormat)) f.channel_layout = channelLayoutAV2FF(frame.ChannelLayout) for i := range frame.Data { - f.data[i] = (*C.uint8_t)(unsafe.Pointer(&frame.Data[i])) + f.data[i] = (*C.uint8_t)(unsafe.Pointer(&frame.Data[i][0])) f.linesize[i] = C.int(len(frame.Data[i])) } } @@ -375,10 +411,7 @@ func freeFFCtx(self *ffctx) { } } -func NewAudioEncoder( - name string, - sampleFormat av.SampleFormat, sampleRate int, channelLayout av.ChannelLayout, bitRate int, -) (enc *AudioEncoder, err error) { +func NewAudioEncoder(name string) (enc *AudioEncoder, err error) { _enc := &AudioEncoder{} codec := C.avcodec_find_encoder_by_name(C.CString(name)) @@ -394,13 +427,6 @@ func NewAudioEncoder( if _enc.ff, err = newFFCtxByCodec(codec); err != nil { return } - _enc.SampleFormat = sampleFormat - _enc.SampleRate = sampleRate - _enc.ChannelLayout = channelLayout - _enc.BitRate = bitRate - if err = _enc.Setup(); err != nil { - return - } enc = _enc return } From 8d1ff3f208c7454279c6d5ec6e2ac8dab995aac3 Mon Sep 17 00:00:00 2001 From: nareix Date: Wed, 1 Jun 2016 18:02:32 +0800 Subject: [PATCH 04/17] Resample can work. add NewAudioEncoderByName() NewAudioEncoderByCodecType() --- audio.go | 268 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 221 insertions(+), 47 deletions(-) diff --git a/audio.go b/audio.go index 3fe4e67..74f7a43 100644 --- a/audio.go +++ b/audio.go @@ -25,50 +25,124 @@ type Resampler struct { inChannelLayout, OutChannelLayout av.ChannelLayout inSampleRate, OutSampleRate int avr *C.AVAudioResampleContext - inframe, outframe *C.AVFrame -} - -func freeResampler(self *Resampler) { - if self.avr != nil { - C.avresample_free(&self.avr) - self.avr = nil - } -} - -func (self *Resampler) setupAvr() { - avr := C.avresample_alloc_context() - C.av_opt_set_int(avr, C.CString("in_channel_layout"), C.int64_t(channelLayoutAV2FF(self.inChannelLayout)), 0) - C.av_opt_set_int(avr, C.CString("out_channel_layout"), C.int64_t(channelLayoutAV2FF(self.OutChannelLayout)), 0) - C.av_opt_set_int(avr, C.CString("in_sample_rate"), C.int64_t(self.inSampleRate), 0) - C.av_opt_set_int(avr, C.CString("out_sample_rate"), C.int64_t(self.OutSampleRate), 0) - C.av_opt_set_int(avr, C.CString("in_sample_fmt"), C.int64_t(sampleFormatAV2FF(self.inSampleFormat)), 0) - C.av_opt_set_int(avr, C.CString("out_sample_fmt"), C.int64_t(sampleFormatAV2FF(self.OutSampleFormat)), 0) - self.avr = avr } func (self *Resampler) Resample(in av.AudioFrame) (out av.AudioFrame, err error) { - if self.inframe == nil { - self.inframe = C.av_frame_alloc() - self.outframe = C.av_frame_alloc() - runtime.SetFinalizer(self, freeResampler) - } - if in.SampleRate != self.inSampleRate || in.SampleFormat != self.inSampleFormat || in.ChannelLayout != self.inChannelLayout { - // TODO: flush left bytes - freeResampler(self) + formatChange := in.SampleRate != self.inSampleRate || in.SampleFormat != self.inSampleFormat || in.ChannelLayout != self.inChannelLayout + + var flush av.AudioFrame + + if formatChange { + if self.avr != nil { + outChannels := self.OutChannelLayout.Count() + if !self.OutSampleFormat.IsPlanar() { + outChannels = 1 + } + outData := make([]*C.uint8_t, outChannels) + outSampleCount := int(C.avresample_get_out_samples(self.avr, C.int(in.SampleCount))) + outLinesize := outSampleCount*self.OutSampleFormat.BytesPerSample() + flush.Data = make([][]byte, outChannels) + for i := 0; i < outChannels; i++ { + flush.Data[i] = make([]byte, outLinesize) + outData[i] = (*C.uint8_t)(unsafe.Pointer(&flush.Data[i][0])) + } + flush.ChannelLayout = self.OutChannelLayout + flush.SampleFormat = self.OutSampleFormat + flush.SampleRate = self.OutSampleRate + + convertSamples := int(C.avresample_convert( + self.avr, + unsafe.Pointer(&outData[0]), C.int(outLinesize), C.int(outSampleCount), + nil, C.int(0), C.int(0), + )) + if convertSamples < 0 { + err = fmt.Errorf("avresample_convert_frame failed") + return + } + flush.SampleCount = convertSamples + if convertSamples < outSampleCount { + for i := 0; i < outChannels; i++ { + flush.Data[i] = flush.Data[i][:convertSamples*self.OutSampleFormat.BytesPerSample()] + } + } + + //fmt.Println("flush:", "outSampleCount", outSampleCount, "convertSamples", convertSamples, "datasize", len(flush.Data[0])) + } else { + runtime.SetFinalizer(self, func(self *Resampler) { + self.Close() + }) + } + + C.avresample_free(&self.avr) self.inSampleFormat = in.SampleFormat self.inSampleRate = in.SampleRate self.inChannelLayout = in.ChannelLayout - self.setupAvr() + avr := C.avresample_alloc_context() + C.av_opt_set_int(unsafe.Pointer(avr), C.CString("in_channel_layout"), C.int64_t(channelLayoutAV2FF(self.inChannelLayout)), 0) + C.av_opt_set_int(unsafe.Pointer(avr), C.CString("out_channel_layout"), C.int64_t(channelLayoutAV2FF(self.OutChannelLayout)), 0) + C.av_opt_set_int(unsafe.Pointer(avr), C.CString("in_sample_rate"), C.int64_t(self.inSampleRate), 0) + C.av_opt_set_int(unsafe.Pointer(avr), C.CString("out_sample_rate"), C.int64_t(self.OutSampleRate), 0) + C.av_opt_set_int(unsafe.Pointer(avr), C.CString("in_sample_fmt"), C.int64_t(sampleFormatAV2FF(self.inSampleFormat)), 0) + C.av_opt_set_int(unsafe.Pointer(avr), C.CString("out_sample_fmt"), C.int64_t(sampleFormatAV2FF(self.OutSampleFormat)), 0) + C.avresample_open(avr) + self.avr = avr } - audioFrameAssignToFF(in, self.inframe) - if C.avresample_convert_frame(self.avr, self.outframe, self.inframe) != 0 { + + inChannels := self.inChannelLayout.Count() + if !self.inSampleFormat.IsPlanar() { + inChannels = 1 + } + inSampleCount := in.SampleCount + inLinesize := inSampleCount*in.SampleFormat.BytesPerSample() + inData := make([]*C.uint8_t, inChannels) + for i := 0; i < inChannels; i++ { + inData[i] = (*C.uint8_t)(unsafe.Pointer(&in.Data[i][0])) + } + + outChannels := self.OutChannelLayout.Count() + if !self.OutSampleFormat.IsPlanar() { + outChannels = 1 + } + outData := make([]*C.uint8_t, outChannels) + outSampleCount := int(C.avresample_get_out_samples(self.avr, C.int(in.SampleCount))) + outLinesize := outSampleCount*self.OutSampleFormat.BytesPerSample() + out.Data = make([][]byte, outChannels) + for i := 0; i < outChannels; i++ { + out.Data[i] = make([]byte, outLinesize) + outData[i] = (*C.uint8_t)(unsafe.Pointer(&out.Data[i][0])) + } + out.ChannelLayout = self.OutChannelLayout + out.SampleFormat = self.OutSampleFormat + out.SampleRate = self.OutSampleRate + + convertSamples := int(C.avresample_convert( + self.avr, + unsafe.Pointer(&outData[0]), C.int(outLinesize), C.int(outSampleCount), + unsafe.Pointer(&inData[0]), C.int(inLinesize), C.int(inSampleCount), + )) + if convertSamples < 0 { err = fmt.Errorf("avresample_convert_frame failed") return } - audioFrameAssignToAV(self.outframe, &out) + out.SampleCount = convertSamples + if convertSamples < outSampleCount { + for i := 0; i < outChannels; i++ { + out.Data[i] = out.Data[i][:convertSamples*self.OutSampleFormat.BytesPerSample()] + } + } + //fmt.Println("outSampleCount", outSampleCount, "convertSamples", convertSamples) + + if flush.SampleCount > 0 { + out = flush.Concat(out) + } + return } +func (self *Resampler) Close() { + C.avresample_free(&self.avr) +} + type AudioEncoder struct { ff *ffctx SampleRate int @@ -150,7 +224,6 @@ func (self *AudioEncoder) Setup() (err error) { self.ChannelLayout = av.CH_STEREO } - //C.set_sample_fmt(ff.codecCtx, C.int(self.SampleFormat)) ff.codecCtx.sample_fmt = sampleFormatAV2FF(self.SampleFormat) ff.codecCtx.sample_rate = C.int(self.SampleRate) ff.codecCtx.bit_rate = C.int(self.BitRate) @@ -190,10 +263,26 @@ func (self *AudioEncoder) CodecData() (codec av.AudioCodecData) { func (self *AudioEncoder) encodeOne(frame av.AudioFrame) (gotpkt bool, pkt av.Packet, err error) { ff := &self.ff.ff + if ff.frame == nil { + if err = self.Setup(); err != nil { + return + } + } + cpkt := C.AVPacket{} cgotpkt := C.int(0) audioFrameAssignToFF(frame, ff.frame) + if false { + farr := []string{} + for i := 0; i < len(frame.Data[0])/4; i++ { + var f *float64 = (*float64)(unsafe.Pointer(&frame.Data[0][i*4])) + farr = append(farr, fmt.Sprintf("%.8f", *f)) + } + fmt.Println(farr) + } + //fmt.Println("encode", ff.frame.channels, ff.frame.nb_samples, ff.frame.sample_rate, ff.frame.channel_layout, ff.frame.linesize[0], len(frame.Data)) + cerr := C.avcodec_encode_audio2(ff.codecCtx, &cpkt, ff.frame, &cgotpkt) if cerr < C.int(0) { err = fmt.Errorf("avcodec_encode_audio2 failed: %d", cerr) @@ -210,10 +299,30 @@ func (self *AudioEncoder) encodeOne(frame av.AudioFrame) (gotpkt bool, pkt av.Pa return } +func (self *AudioEncoder) resample(in av.AudioFrame) (out av.AudioFrame, err error) { + if self.resampler == nil { + self.resampler = &Resampler{ + OutSampleFormat: self.SampleFormat, + OutSampleRate: self.SampleRate, + OutChannelLayout: self.ChannelLayout, + } + } + if out, err = self.resampler.Resample(in); err != nil { + return + } + return +} + func (self *AudioEncoder) Encode(frame av.AudioFrame) (pkts []av.Packet, err error) { var gotpkt bool var pkt av.Packet + if frame.SampleFormat != self.SampleFormat || frame.ChannelLayout != self.ChannelLayout || frame.SampleRate != self.SampleRate { + if frame, err = self.resample(frame); err != nil { + return + } + } + if self.FrameSampleCount != 0 { if self.framebuf.SampleCount == 0 { self.framebuf = frame @@ -244,29 +353,51 @@ func (self *AudioEncoder) Encode(frame av.AudioFrame) (pkts []av.Packet, err err func (self *AudioEncoder) Close() { freeFFCtx(self.ff) + if self.resampler != nil { + self.resampler.Close() + self.resampler = nil + } } -func audioFrameAssignToAV(f *C.AVFrame, frame *av.AudioFrame) { - frame.SampleCount = int(f.nb_samples) +func audioFrameAssignToAVParams(f *C.AVFrame, frame *av.AudioFrame) { frame.SampleFormat = sampleFormatFF2AV(int32(f.format)) frame.ChannelLayout = channelLayoutFF2AV(f.channel_layout) - channels := int(f.channels) - frame.Data = make([][]byte, channels) - for i := 0; i < channels; i++ { + frame.SampleRate = int(f.sample_rate) +} + +func audioFrameAssignToAVData(f *C.AVFrame, frame *av.AudioFrame) { + frame.SampleCount = int(f.nb_samples) + frame.Data = make([][]byte, int(f.channels)) + for i := 0; i < int(f.channels); i++ { frame.Data[i] = C.GoBytes(unsafe.Pointer(f.data[i]), f.linesize[i]) } } -func audioFrameAssignToFF(frame av.AudioFrame, f *C.AVFrame) { - f.nb_samples = C.int(frame.SampleCount) +func audioFrameAssignToAV(f *C.AVFrame, frame *av.AudioFrame) { + audioFrameAssignToAVParams(f, frame) + audioFrameAssignToAVData(f, frame) +} + +func audioFrameAssignToFFParams(frame av.AudioFrame, f *C.AVFrame) { f.format = C.int(sampleFormatAV2FF(frame.SampleFormat)) f.channel_layout = channelLayoutAV2FF(frame.ChannelLayout) + f.sample_rate = C.int(frame.SampleRate) + f.channels = C.int(frame.ChannelLayout.Count()) +} + +func audioFrameAssignToFFData(frame av.AudioFrame, f *C.AVFrame) { + f.nb_samples = C.int(frame.SampleCount) for i := range frame.Data { f.data[i] = (*C.uint8_t)(unsafe.Pointer(&frame.Data[i][0])) f.linesize[i] = C.int(len(frame.Data[i])) } } +func audioFrameAssignToFF(frame av.AudioFrame, f *C.AVFrame) { + audioFrameAssignToFFParams(frame, f) + audioFrameAssignToFFData(frame, f) +} + func channelLayoutFF2AV(layout C.uint64_t) (channelLayout av.ChannelLayout) { if layout & C.AV_CH_FRONT_CENTER != 0 { channelLayout |= av.CH_FRONT_CENTER @@ -333,6 +464,7 @@ type AudioDecoder struct { ff *ffctx ChannelLayout av.ChannelLayout SampleFormat av.SampleFormat + SampleRate int Extradata []byte } @@ -346,30 +478,36 @@ func (self *AudioDecoder) Setup() (err error) { ff.codecCtx.extradata_size = C.int(len(self.Extradata)) } + ff.codecCtx.sample_rate = C.int(self.SampleRate) ff.codecCtx.channel_layout = channelLayoutAV2FF(self.ChannelLayout) + ff.codecCtx.channels = C.int(self.ChannelLayout.Count()) if C.avcodec_open2(ff.codecCtx, ff.codec, nil) != 0 { err = fmt.Errorf("avcodec_open2 failed") return } self.SampleFormat = sampleFormatFF2AV(ff.codecCtx.sample_fmt) self.ChannelLayout = channelLayoutFF2AV(ff.codecCtx.channel_layout) + if self.SampleRate == 0 { + self.SampleRate = int(ff.codecCtx.sample_rate) + } return } -func (self *AudioDecoder) Decode(data []byte) (gotFrame bool, frame av.AudioFrame, err error) { +func (self *AudioDecoder) Decode(data []byte) (gotframe bool, frame av.AudioFrame, err error) { ff := &self.ff.ff - cgotpkt := C.int(0) - cerr := C.wrap_avcodec_decode_audio4(ff.codecCtx, ff.frame, unsafe.Pointer(&data[0]), C.int(len(data)), &cgotpkt) + cgotframe := C.int(0) + cerr := C.wrap_avcodec_decode_audio4(ff.codecCtx, ff.frame, unsafe.Pointer(&data[0]), C.int(len(data)), &cgotframe) if cerr < C.int(0) { err = fmt.Errorf("avcodec_decode_audio4 failed: %d", cerr) return } - if cgotpkt != C.int(0) { - gotFrame = true + if cgotframe != C.int(0) { + gotframe = true audioFrameAssignToAV(ff.frame, &frame) + frame.SampleRate = self.SampleRate } return @@ -411,7 +549,38 @@ func freeFFCtx(self *ffctx) { } } -func NewAudioEncoder(name string) (enc *AudioEncoder, err error) { +func NewAudioEncoderByCodecType(typ int) (enc *AudioEncoder, err error) { + var id uint32 + + switch typ { + case av.AAC: + id = C.AV_CODEC_ID_AAC + + default: + err = fmt.Errorf("cannot find encoder codecType=%d", typ) + return + } + + codec := C.avcodec_find_encoder(id) + if codec == nil { + err = fmt.Errorf("cannot find encoder codecId=%d", id) + return + } + + if C.avcodec_get_type(id) != C.AVMEDIA_TYPE_AUDIO { + err = fmt.Errorf("codecId=%d is not audio", id) + return + } + + _enc := &AudioEncoder{} + if _enc.ff, err = newFFCtxByCodec(codec); err != nil { + return + } + enc = _enc + return +} + +func NewAudioEncoderByName(name string) (enc *AudioEncoder, err error) { _enc := &AudioEncoder{} codec := C.avcodec_find_encoder_by_name(C.CString(name)) @@ -445,10 +614,11 @@ func NewAudioDecoder(codec av.AudioCodecData) (dec *AudioDecoder, err error) { return } + case av.PCM_MULAW: + id = C.AV_CODEC_ID_PCM_MULAW + default: if ffcodec, ok := codec.(AudioCodecData); ok { - _dec.SampleFormat = ffcodec.sampleFormat - _dec.ChannelLayout = ffcodec.channelLayout _dec.Extradata = ffcodec.extradata id = ffcodec.codecId } else { @@ -471,6 +641,10 @@ func NewAudioDecoder(codec av.AudioCodecData) (dec *AudioDecoder, err error) { if _dec.ff, err = newFFCtxByCodec(c); err != nil { return } + + _dec.SampleFormat = codec.SampleFormat() + _dec.SampleRate = codec.SampleRate() + _dec.ChannelLayout = codec.ChannelLayout() if err = _dec.Setup(); err != nil { return } From 486862d5e1057bd0c5d374bee8c75751ba90d564 Mon Sep 17 00:00:00 2001 From: nareix Date: Mon, 6 Jun 2016 16:48:12 +0800 Subject: [PATCH 05/17] add SetLogLevel --- ffmpeg.go | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/ffmpeg.go b/ffmpeg.go index b9b5468..04f596c 100644 --- a/ffmpeg.go +++ b/ffmpeg.go @@ -5,11 +5,26 @@ package ffmpeg #include "ffmpeg.h" void ffinit() { av_register_all(); - av_log_set_level(AV_LOG_DEBUG); } */ import "C" +const ( + QUIET = int(C.AV_LOG_QUIET) + PANIC = int(C.AV_LOG_PANIC) + FATAL = int(C.AV_LOG_FATAL) + ERROR = int(C.AV_LOG_ERROR) + WARNING = int(C.AV_LOG_WARNING) + INFO = int(C.AV_LOG_INFO) + VERBOSE = int(C.AV_LOG_VERBOSE) + DEBUG = int(C.AV_LOG_DEBUG) + TRACE = int(C.AV_LOG_TRACE) +) + +func SetLogLevel(level int) { + C.av_log_set_level(C.int(level)) +} + func init() { C.ffinit() } From a49ccdb0caed2c85ce336866ff68e2200789d5be Mon Sep 17 00:00:00 2001 From: nareix Date: Mon, 6 Jun 2016 16:49:05 +0800 Subject: [PATCH 06/17] choose default sample format for AudioEncoder --- audio.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/audio.go b/audio.go index 74f7a43..5c2194f 100644 --- a/audio.go +++ b/audio.go @@ -211,11 +211,12 @@ 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 + self.SampleFormat = sampleFormatFF2AV(*ff.codec.sample_fmts) } if self.BitRate == 0 { - self.BitRate = 50000 + self.BitRate = 80000 } if self.SampleRate == 0 { self.SampleRate = 44100 From ff6d406d5e3c94d6f31beb46131ebe7dfc38ce86 Mon Sep 17 00:00:00 2001 From: nareix Date: Tue, 7 Jun 2016 19:32:31 +0800 Subject: [PATCH 07/17] bugfix: only f.linesize[0] is valid --- audio.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/audio.go b/audio.go index 5c2194f..0441ea6 100644 --- a/audio.go +++ b/audio.go @@ -16,6 +16,8 @@ import ( "github.com/nareix/codec/aacparser" ) +const debug = false + type ffctx struct { ff C.FFCtx } @@ -370,7 +372,7 @@ func audioFrameAssignToAVData(f *C.AVFrame, frame *av.AudioFrame) { frame.SampleCount = int(f.nb_samples) frame.Data = make([][]byte, int(f.channels)) for i := 0; i < int(f.channels); i++ { - frame.Data[i] = C.GoBytes(unsafe.Pointer(f.data[i]), f.linesize[i]) + frame.Data[i] = C.GoBytes(unsafe.Pointer(f.data[i]), f.linesize[0]) } } @@ -478,6 +480,9 @@ func (self *AudioDecoder) Setup() (err error) { ff.codecCtx.extradata = (*C.uint8_t)(unsafe.Pointer(&self.Extradata[0])) ff.codecCtx.extradata_size = C.int(len(self.Extradata)) } + if debug { + fmt.Println("ffmpeg: Decoder.Setup Extradata.len", len(self.Extradata)) + } ff.codecCtx.sample_rate = C.int(self.SampleRate) ff.codecCtx.channel_layout = channelLayoutAV2FF(self.ChannelLayout) @@ -500,6 +505,7 @@ func (self *AudioDecoder) Decode(data []byte) (gotframe bool, frame av.AudioFram cgotframe := C.int(0) cerr := C.wrap_avcodec_decode_audio4(ff.codecCtx, ff.frame, unsafe.Pointer(&data[0]), C.int(len(data)), &cgotframe) + if cerr < C.int(0) { err = fmt.Errorf("avcodec_decode_audio4 failed: %d", cerr) return @@ -509,6 +515,10 @@ func (self *AudioDecoder) Decode(data []byte) (gotframe bool, frame av.AudioFram gotframe = true audioFrameAssignToAV(ff.frame, &frame) frame.SampleRate = self.SampleRate + + if debug { + fmt.Println("ffmpeg: Decode", fmt.Sprintf("%x", data[:8]), "len", len(data)) + } } return From 427a2d04d3b428fc27dd6791752d507cdcb6011d Mon Sep 17 00:00:00 2001 From: nareix Date: Wed, 8 Jun 2016 16:48:40 +0800 Subject: [PATCH 08/17] add wrap_avresample_convert() avoid cgo pointer check --- audio.go | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/audio.go b/audio.go index 0441ea6..128d51e 100644 --- a/audio.go +++ b/audio.go @@ -3,10 +3,16 @@ 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); } + + int wrap_avresample_convert(AVAudioResampleContext *avr, int *out, int outsize, int outcount, int *in, int insize, int incount) { + return avresample_convert(avr, (void *)out, outsize, outcount, (void *)in, insize, incount); + } + */ "C" "unsafe" @@ -52,9 +58,9 @@ func (self *Resampler) Resample(in av.AudioFrame) (out av.AudioFrame, err error) flush.SampleFormat = self.OutSampleFormat flush.SampleRate = self.OutSampleRate - convertSamples := int(C.avresample_convert( + convertSamples := int(C.wrap_avresample_convert( self.avr, - unsafe.Pointer(&outData[0]), C.int(outLinesize), C.int(outSampleCount), + (*C.int)(unsafe.Pointer(&outData[0])), C.int(outLinesize), C.int(outSampleCount), nil, C.int(0), C.int(0), )) if convertSamples < 0 { @@ -117,10 +123,10 @@ func (self *Resampler) Resample(in av.AudioFrame) (out av.AudioFrame, err error) out.SampleFormat = self.OutSampleFormat out.SampleRate = self.OutSampleRate - convertSamples := int(C.avresample_convert( + convertSamples := int(C.wrap_avresample_convert( self.avr, - unsafe.Pointer(&outData[0]), C.int(outLinesize), C.int(outSampleCount), - unsafe.Pointer(&inData[0]), C.int(inLinesize), C.int(inSampleCount), + (*C.int)(unsafe.Pointer(&outData[0])), C.int(outLinesize), C.int(outSampleCount), + (*C.int)(unsafe.Pointer(&inData[0])), C.int(inLinesize), C.int(inSampleCount), )) if convertSamples < 0 { err = fmt.Errorf("avresample_convert_frame failed") @@ -132,7 +138,6 @@ func (self *Resampler) Resample(in av.AudioFrame) (out av.AudioFrame, err error) out.Data[i] = out.Data[i][:convertSamples*self.OutSampleFormat.BytesPerSample()] } } - //fmt.Println("outSampleCount", outSampleCount, "convertSamples", convertSamples) if flush.SampleCount > 0 { out = flush.Concat(out) @@ -229,11 +234,11 @@ func (self *AudioEncoder) Setup() (err error) { ff.codecCtx.sample_fmt = sampleFormatAV2FF(self.SampleFormat) ff.codecCtx.sample_rate = C.int(self.SampleRate) - ff.codecCtx.bit_rate = C.int(self.BitRate) + ff.codecCtx.bit_rate = C.int64_t(self.BitRate) ff.codecCtx.channel_layout = channelLayoutAV2FF(self.ChannelLayout) 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") + err = fmt.Errorf("ffmpeg: encoder: avcodec_open2 failed") return } self.SampleFormat = sampleFormatFF2AV(ff.codecCtx.sample_fmt) @@ -284,8 +289,6 @@ func (self *AudioEncoder) encodeOne(frame av.AudioFrame) (gotpkt bool, pkt av.Pa } fmt.Println(farr) } - //fmt.Println("encode", ff.frame.channels, ff.frame.nb_samples, ff.frame.sample_rate, ff.frame.channel_layout, ff.frame.linesize[0], len(frame.Data)) - cerr := C.avcodec_encode_audio2(ff.codecCtx, &cpkt, ff.frame, &cgotpkt) if cerr < C.int(0) { err = fmt.Errorf("avcodec_encode_audio2 failed: %d", cerr) @@ -296,7 +299,11 @@ func (self *AudioEncoder) encodeOne(frame av.AudioFrame) (gotpkt bool, pkt av.Pa gotpkt = true pkt.Data = C.GoBytes(unsafe.Pointer(cpkt.data), cpkt.size) pkt.Duration = float64(frame.SampleCount)/float64(self.SampleRate) - C.av_free_packet(&cpkt) + C.av_packet_unref(&cpkt) + + if debug { + fmt.Println("ffmpeg: Encode", frame.SampleCount, frame.SampleRate, frame.ChannelLayout, frame.SampleFormat, "pkt", len(pkt.Data), "dur", pkt.Duration) + } } return @@ -488,7 +495,7 @@ func (self *AudioDecoder) Setup() (err error) { ff.codecCtx.channel_layout = channelLayoutAV2FF(self.ChannelLayout) ff.codecCtx.channels = C.int(self.ChannelLayout.Count()) if C.avcodec_open2(ff.codecCtx, ff.codec, nil) != 0 { - err = fmt.Errorf("avcodec_open2 failed") + err = fmt.Errorf("ffmpeg: decoder: avcodec_open2 failed") return } self.SampleFormat = sampleFormatFF2AV(ff.codecCtx.sample_fmt) @@ -517,7 +524,7 @@ func (self *AudioDecoder) Decode(data []byte) (gotframe bool, frame av.AudioFram frame.SampleRate = self.SampleRate if debug { - fmt.Println("ffmpeg: Decode", fmt.Sprintf("%x", data[:8]), "len", len(data)) + fmt.Println("ffmpeg: Decode", frame.SampleCount, frame.SampleRate, frame.ChannelLayout, frame.SampleFormat) } } @@ -617,7 +624,7 @@ func NewAudioDecoder(codec av.AudioCodecData) (dec *AudioDecoder, err error) { switch codec.Type() { case av.AAC: - if aaccodec, ok := codec.(av.AACCodecData); ok { + if aaccodec, ok := codec.(aacparser.CodecData); ok { _dec.Extradata = aaccodec.MPEG4AudioConfigBytes() id = C.AV_CODEC_ID_AAC } else { From f4f2dc5f020a44575c73a56f54f8d54e8044a66d Mon Sep 17 00:00:00 2001 From: nareix Date: Wed, 8 Jun 2016 19:48:41 +0800 Subject: [PATCH 09/17] fix freeFFCtx() pointer type bug --- audio.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audio.go b/audio.go index 128d51e..ddca6a3 100644 --- a/audio.go +++ b/audio.go @@ -562,7 +562,7 @@ func freeFFCtx(self *ffctx) { } if ff.codecCtx != nil { C.avcodec_close(ff.codecCtx) - C.av_free(ff.codecCtx) + C.av_free(unsafe.Pointer(ff.codecCtx)) ff.codecCtx = nil } } From 3926d8caced5672a4a53652ac205223698d9099b Mon Sep 17 00:00:00 2001 From: nareix Date: Sat, 11 Jun 2016 23:32:39 +0800 Subject: [PATCH 10/17] support av.PCM_ALAW --- audio.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/audio.go b/audio.go index ddca6a3..8741641 100644 --- a/audio.go +++ b/audio.go @@ -635,6 +635,10 @@ func NewAudioDecoder(codec av.AudioCodecData) (dec *AudioDecoder, err error) { case av.PCM_MULAW: id = C.AV_CODEC_ID_PCM_MULAW + + case av.PCM_ALAW: + id = C.AV_CODEC_ID_PCM_ALAW + default: if ffcodec, ok := codec.(AudioCodecData); ok { _dec.Extradata = ffcodec.extradata From 54c98210dda4b48bb403b463eae240b4abb4bac4 Mon Sep 17 00:00:00 2001 From: nareix Date: Sun, 12 Jun 2016 09:01:54 +0800 Subject: [PATCH 11/17] add error msg --- audio.go | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/audio.go b/audio.go index 8741641..016463f 100644 --- a/audio.go +++ b/audio.go @@ -64,7 +64,7 @@ func (self *Resampler) Resample(in av.AudioFrame) (out av.AudioFrame, err error) nil, C.int(0), C.int(0), )) if convertSamples < 0 { - err = fmt.Errorf("avresample_convert_frame failed") + err = fmt.Errorf("ffmpeg: avresample_convert_frame failed") return } flush.SampleCount = convertSamples @@ -129,7 +129,7 @@ func (self *Resampler) Resample(in av.AudioFrame) (out av.AudioFrame, err error) (*C.int)(unsafe.Pointer(&inData[0])), C.int(inLinesize), C.int(inSampleCount), )) if convertSamples < 0 { - err = fmt.Errorf("avresample_convert_frame failed") + err = fmt.Errorf("ffmpeg: avresample_convert_frame failed") return } out.SampleCount = convertSamples @@ -291,7 +291,7 @@ func (self *AudioEncoder) encodeOne(frame av.AudioFrame) (gotpkt bool, pkt av.Pa } cerr := C.avcodec_encode_audio2(ff.codecCtx, &cpkt, ff.frame, &cgotpkt) if cerr < C.int(0) { - err = fmt.Errorf("avcodec_encode_audio2 failed: %d", cerr) + err = fmt.Errorf("ffmpeg: avcodec_encode_audio2 failed: %d", cerr) return } @@ -514,7 +514,7 @@ func (self *AudioDecoder) Decode(data []byte) (gotframe bool, frame av.AudioFram cerr := C.wrap_avcodec_decode_audio4(ff.codecCtx, ff.frame, unsafe.Pointer(&data[0]), C.int(len(data)), &cgotframe) if cerr < C.int(0) { - err = fmt.Errorf("avcodec_decode_audio4 failed: %d", cerr) + err = fmt.Errorf("ffmpeg: avcodec_decode_audio4 failed: %d", cerr) return } @@ -575,18 +575,18 @@ func NewAudioEncoderByCodecType(typ int) (enc *AudioEncoder, err error) { id = C.AV_CODEC_ID_AAC default: - err = fmt.Errorf("cannot find encoder codecType=%d", typ) + err = fmt.Errorf("ffmpeg: cannot find encoder codecType=%d", typ) return } codec := C.avcodec_find_encoder(id) if codec == nil { - err = fmt.Errorf("cannot find encoder codecId=%d", id) + err = fmt.Errorf("ffmpeg: cannot find encoder codecId=%d", id) return } if C.avcodec_get_type(id) != C.AVMEDIA_TYPE_AUDIO { - err = fmt.Errorf("codecId=%d is not audio", id) + err = fmt.Errorf("ffmpeg: codecId=%d is not audio", id) return } @@ -603,11 +603,11 @@ func NewAudioEncoderByName(name string) (enc *AudioEncoder, err error) { codec := C.avcodec_find_encoder_by_name(C.CString(name)) if codec == nil { - err = fmt.Errorf("cannot find encoder=%s", name) + err = fmt.Errorf("ffmpeg: cannot find encoder=%s", name) return } if C.avcodec_get_type(codec.id) != C.AVMEDIA_TYPE_AUDIO { - err = fmt.Errorf("encoder=%s type is not audio", name) + err = fmt.Errorf("ffmpeg: encoder=%s type is not audio", name) return } @@ -628,7 +628,7 @@ func NewAudioDecoder(codec av.AudioCodecData) (dec *AudioDecoder, err error) { _dec.Extradata = aaccodec.MPEG4AudioConfigBytes() id = C.AV_CODEC_ID_AAC } else { - err = fmt.Errorf("aac CodecData must be av.AACCodecData") + err = fmt.Errorf("ffmpeg: aac CodecData must be av.AACCodecData") return } @@ -644,19 +644,19 @@ func NewAudioDecoder(codec av.AudioCodecData) (dec *AudioDecoder, err error) { _dec.Extradata = ffcodec.extradata id = ffcodec.codecId } else { - err = fmt.Errorf("invalid CodecData for ffmpeg to decode") + err = fmt.Errorf("ffmpeg: invalid CodecData for ffmpeg to decode") return } } c := C.avcodec_find_decoder(id) if c == nil { - err = fmt.Errorf("cannot find decoder id=%d", id) + err = fmt.Errorf("ffmpeg: cannot find decoder id=%d", id) return } if C.avcodec_get_type(c.id) != C.AVMEDIA_TYPE_AUDIO { - err = fmt.Errorf("decoder id=%d type is not audio", c.id) + err = fmt.Errorf("ffmpeg: decoder id=%d type is not audio", c.id) return } From 3c6c076e442acec45aabbf53cafa3d2b168fd252 Mon Sep 17 00:00:00 2001 From: nareix Date: Sun, 12 Jun 2016 16:07:28 +0800 Subject: [PATCH 12/17] split ffctx to single file --- audio.go | 50 +++++++++++--------------------------------------- ffmpeg.go | 29 +++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 39 deletions(-) diff --git a/audio.go b/audio.go index 016463f..eb32e70 100644 --- a/audio.go +++ b/audio.go @@ -1,20 +1,17 @@ package ffmpeg +/* +#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); +} +int wrap_avresample_convert(AVAudioResampleContext *avr, int *out, int outsize, int outcount, int *in, int insize, int incount) { + return avresample_convert(avr, (void *)out, outsize, outcount, (void *)in, insize, incount); +} +*/ +import "C" 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); - } - - int wrap_avresample_convert(AVAudioResampleContext *avr, int *out, int outsize, int outcount, int *in, int insize, int incount) { - return avresample_convert(avr, (void *)out, outsize, outcount, (void *)in, insize, incount); - } - - */ - "C" "unsafe" "runtime" "fmt" @@ -24,10 +21,6 @@ import ( const debug = false -type ffctx struct { - ff C.FFCtx -} - type Resampler struct { inSampleFormat, OutSampleFormat av.SampleFormat inChannelLayout, OutChannelLayout av.ChannelLayout @@ -546,27 +539,6 @@ func HasDecoder(name string) bool { //func EncodersList() []string //func DecodersList() []string -func newFFCtxByCodec(codec *C.AVCodec) (ff *ffctx, err error) { - 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(unsafe.Pointer(ff.codecCtx)) - ff.codecCtx = nil - } -} - func NewAudioEncoderByCodecType(typ int) (enc *AudioEncoder, err error) { var id uint32 diff --git a/ffmpeg.go b/ffmpeg.go index 04f596c..d217f03 100644 --- a/ffmpeg.go +++ b/ffmpeg.go @@ -8,6 +8,10 @@ void ffinit() { } */ import "C" +import ( + "runtime" + "unsafe" +) const ( QUIET = int(C.AV_LOG_QUIET) @@ -29,3 +33,28 @@ func init() { C.ffinit() } +type ffctx struct { + ff C.FFCtx +} + +func newFFCtxByCodec(codec *C.AVCodec) (ff *ffctx, err error) { + 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(unsafe.Pointer(ff.codecCtx)) + ff.codecCtx = nil + } +} + From f03fbb1382b0bc69ef56c5d7f5e872d42f96da48 Mon Sep 17 00:00:00 2001 From: nareix Date: Sun, 12 Jun 2016 17:57:31 +0800 Subject: [PATCH 13/17] hide AudioCodecData --- audio.go | 46 ++++++++++++++++------------------------------ 1 file changed, 16 insertions(+), 30 deletions(-) diff --git a/audio.go b/audio.go index eb32e70..e5fcc19 100644 --- a/audio.go +++ b/audio.go @@ -245,7 +245,7 @@ func (self *AudioEncoder) Setup() (err error) { } default: - self.codecData = AudioCodecData{ + self.codecData = audioCodecData{ channelLayout: self.ChannelLayout, sampleFormat: self.SampleFormat, sampleRate: self.SampleRate, @@ -552,13 +552,8 @@ func NewAudioEncoderByCodecType(typ int) (enc *AudioEncoder, err error) { } codec := C.avcodec_find_encoder(id) - if codec == nil { - err = fmt.Errorf("ffmpeg: cannot find encoder codecId=%d", id) - return - } - - if C.avcodec_get_type(id) != C.AVMEDIA_TYPE_AUDIO { - err = fmt.Errorf("ffmpeg: codecId=%d is not audio", id) + if codec == nil || C.avcodec_get_type(id) != C.AVMEDIA_TYPE_AUDIO { + err = fmt.Errorf("ffmpeg: cannot find audio encoder codecId=%d", id) return } @@ -574,12 +569,8 @@ func NewAudioEncoderByName(name string) (enc *AudioEncoder, err error) { _enc := &AudioEncoder{} codec := C.avcodec_find_encoder_by_name(C.CString(name)) - if codec == nil { - err = fmt.Errorf("ffmpeg: cannot find encoder=%s", name) - return - } - if C.avcodec_get_type(codec.id) != C.AVMEDIA_TYPE_AUDIO { - err = fmt.Errorf("ffmpeg: encoder=%s type is not audio", name) + if codec == nil || C.avcodec_get_type(codec.id) != C.AVMEDIA_TYPE_AUDIO { + err = fmt.Errorf("ffmpeg: cannot find audio encoder name=%s", name) return } @@ -600,7 +591,7 @@ func NewAudioDecoder(codec av.AudioCodecData) (dec *AudioDecoder, err error) { _dec.Extradata = aaccodec.MPEG4AudioConfigBytes() id = C.AV_CODEC_ID_AAC } else { - err = fmt.Errorf("ffmpeg: aac CodecData must be av.AACCodecData") + err = fmt.Errorf("ffmpeg: aac CodecData must be aacparser.CodecData") return } @@ -612,7 +603,7 @@ func NewAudioDecoder(codec av.AudioCodecData) (dec *AudioDecoder, err error) { id = C.AV_CODEC_ID_PCM_ALAW default: - if ffcodec, ok := codec.(AudioCodecData); ok { + if ffcodec, ok := codec.(audioCodecData); ok { _dec.Extradata = ffcodec.extradata id = ffcodec.codecId } else { @@ -622,13 +613,8 @@ func NewAudioDecoder(codec av.AudioCodecData) (dec *AudioDecoder, err error) { } c := C.avcodec_find_decoder(id) - if c == nil { - err = fmt.Errorf("ffmpeg: cannot find decoder id=%d", id) - return - } - - if C.avcodec_get_type(c.id) != C.AVMEDIA_TYPE_AUDIO { - err = fmt.Errorf("ffmpeg: decoder id=%d type is not audio", c.id) + if c == nil || C.avcodec_get_type(c.id) != C.AVMEDIA_TYPE_AUDIO { + err = fmt.Errorf("ffmpeg: cannot find audio decoder id=%d", id) return } @@ -647,7 +633,7 @@ func NewAudioDecoder(codec av.AudioCodecData) (dec *AudioDecoder, err error) { return } -type AudioCodecData struct { +type audioCodecData struct { codecId uint32 sampleFormat av.SampleFormat channelLayout av.ChannelLayout @@ -655,27 +641,27 @@ type AudioCodecData struct { extradata []byte } -func (self AudioCodecData) Type() int { +func (self audioCodecData) Type() int { return int(self.codecId) } -func (self AudioCodecData) IsAudio() bool { +func (self audioCodecData) IsAudio() bool { return true } -func (self AudioCodecData) IsVideo() bool { +func (self audioCodecData) IsVideo() bool { return false } -func (self AudioCodecData) SampleRate() int { +func (self audioCodecData) SampleRate() int { return self.sampleRate } -func (self AudioCodecData) SampleFormat() av.SampleFormat { +func (self audioCodecData) SampleFormat() av.SampleFormat { return self.sampleFormat } -func (self AudioCodecData) ChannelLayout() av.ChannelLayout { +func (self audioCodecData) ChannelLayout() av.ChannelLayout { return self.channelLayout } From 01bf4218c98a3841a70564a81b30a4e7504c680f Mon Sep 17 00:00:00 2001 From: nareix Date: Sat, 18 Jun 2016 10:25:57 +0800 Subject: [PATCH 14/17] switch to time.Duration and new av.CodecType --- audio.go | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/audio.go b/audio.go index e5fcc19..9035a96 100644 --- a/audio.go +++ b/audio.go @@ -15,6 +15,7 @@ import ( "unsafe" "runtime" "fmt" + "time" "github.com/nareix/av" "github.com/nareix/codec/aacparser" ) @@ -291,7 +292,7 @@ func (self *AudioEncoder) encodeOne(frame av.AudioFrame) (gotpkt bool, pkt av.Pa if cgotpkt != 0 { gotpkt = true pkt.Data = C.GoBytes(unsafe.Pointer(cpkt.data), cpkt.size) - pkt.Duration = float64(frame.SampleCount)/float64(self.SampleRate) + pkt.Duration = time.Duration(frame.SampleCount)*time.Second/time.Duration(self.SampleRate) C.av_packet_unref(&cpkt) if debug { @@ -500,12 +501,11 @@ func (self *AudioDecoder) Setup() (err error) { return } -func (self *AudioDecoder) Decode(data []byte) (gotframe bool, frame av.AudioFrame, err error) { +func (self *AudioDecoder) Decode(pkt []byte) (gotframe bool, frame av.AudioFrame, err error) { ff := &self.ff.ff cgotframe := C.int(0) - cerr := C.wrap_avcodec_decode_audio4(ff.codecCtx, ff.frame, unsafe.Pointer(&data[0]), C.int(len(data)), &cgotframe) - + cerr := C.wrap_avcodec_decode_audio4(ff.codecCtx, ff.frame, unsafe.Pointer(&pkt[0]), C.int(len(pkt)), &cgotframe) if cerr < C.int(0) { err = fmt.Errorf("ffmpeg: avcodec_decode_audio4 failed: %d", cerr) return @@ -539,7 +539,7 @@ func HasDecoder(name string) bool { //func EncodersList() []string //func DecodersList() []string -func NewAudioEncoderByCodecType(typ int) (enc *AudioEncoder, err error) { +func NewAudioEncoderByCodecType(typ av.CodecType) (enc *AudioEncoder, err error) { var id uint32 switch typ { @@ -641,16 +641,8 @@ type audioCodecData struct { 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) Type() av.CodecType { + return av.MakeAudioCodecType(self.codecId) } func (self audioCodecData) SampleRate() int { From 77c2d0ba6945c095f6c4ff14ca5aab2b2ea70417 Mon Sep 17 00:00:00 2001 From: nareix Date: Wed, 22 Jun 2016 12:59:38 +0800 Subject: [PATCH 15/17] change to pkt.Time --- audio.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/audio.go b/audio.go index 9035a96..8d85394 100644 --- a/audio.go +++ b/audio.go @@ -292,11 +292,11 @@ func (self *AudioEncoder) encodeOne(frame av.AudioFrame) (gotpkt bool, pkt av.Pa if cgotpkt != 0 { gotpkt = true pkt.Data = C.GoBytes(unsafe.Pointer(cpkt.data), cpkt.size) - pkt.Duration = time.Duration(frame.SampleCount)*time.Second/time.Duration(self.SampleRate) + pkt.Time = time.Duration(frame.SampleCount)*time.Second/time.Duration(self.SampleRate) C.av_packet_unref(&cpkt) if debug { - fmt.Println("ffmpeg: Encode", frame.SampleCount, frame.SampleRate, frame.ChannelLayout, frame.SampleFormat, "pkt", len(pkt.Data), "dur", pkt.Duration) + fmt.Println("ffmpeg: Encode", frame.SampleCount, frame.SampleRate, frame.ChannelLayout, frame.SampleFormat, "pkt", len(pkt.Data), "dur", pkt.Time) } } From 99334d2a92307d4e6a8f16058c10b38aeac1093d Mon Sep 17 00:00:00 2001 From: nareix Date: Wed, 22 Jun 2016 17:58:29 +0800 Subject: [PATCH 16/17] add PacketDuration --- audio.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/audio.go b/audio.go index 8d85394..03640e3 100644 --- a/audio.go +++ b/audio.go @@ -292,11 +292,10 @@ func (self *AudioEncoder) encodeOne(frame av.AudioFrame) (gotpkt bool, pkt av.Pa if cgotpkt != 0 { gotpkt = true pkt.Data = C.GoBytes(unsafe.Pointer(cpkt.data), cpkt.size) - pkt.Time = time.Duration(frame.SampleCount)*time.Second/time.Duration(self.SampleRate) C.av_packet_unref(&cpkt) if debug { - fmt.Println("ffmpeg: Encode", frame.SampleCount, frame.SampleRate, frame.ChannelLayout, frame.SampleFormat, "pkt", len(pkt.Data), "dur", pkt.Time) + fmt.Println("ffmpeg: Encode", frame.SampleCount, frame.SampleRate, frame.ChannelLayout, frame.SampleFormat, "len", len(pkt.Data)) } } @@ -657,3 +656,9 @@ func (self audioCodecData) ChannelLayout() av.ChannelLayout { return self.channelLayout } +func (self audioCodecData) PacketDuration(data []byte) (dur time.Duration, err error) { + // TODO: implement it + err = fmt.Errorf("ffmpeg: cannot get packet duration") + return +} + From 4776c64d2f86b0f104a307b268f9d380442ddfba Mon Sep 17 00:00:00 2001 From: nareix Date: Wed, 22 Jun 2016 22:33:31 +0800 Subject: [PATCH 17/17] interface change av.Packet -> []byte --- audio.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/audio.go b/audio.go index 03640e3..2e93e3f 100644 --- a/audio.go +++ b/audio.go @@ -262,7 +262,7 @@ func (self *AudioEncoder) CodecData() (codec av.AudioCodecData) { return self.codecData } -func (self *AudioEncoder) encodeOne(frame av.AudioFrame) (gotpkt bool, pkt av.Packet, err error) { +func (self *AudioEncoder) encodeOne(frame av.AudioFrame) (gotpkt bool, pkt []byte, err error) { ff := &self.ff.ff if ff.frame == nil { @@ -291,11 +291,11 @@ func (self *AudioEncoder) encodeOne(frame av.AudioFrame) (gotpkt bool, pkt av.Pa if cgotpkt != 0 { gotpkt = true - pkt.Data = C.GoBytes(unsafe.Pointer(cpkt.data), cpkt.size) + pkt = C.GoBytes(unsafe.Pointer(cpkt.data), cpkt.size) C.av_packet_unref(&cpkt) if debug { - fmt.Println("ffmpeg: Encode", frame.SampleCount, frame.SampleRate, frame.ChannelLayout, frame.SampleFormat, "len", len(pkt.Data)) + fmt.Println("ffmpeg: Encode", frame.SampleCount, frame.SampleRate, frame.ChannelLayout, frame.SampleFormat, "len", len(pkt)) } } @@ -316,9 +316,9 @@ func (self *AudioEncoder) resample(in av.AudioFrame) (out av.AudioFrame, err err return } -func (self *AudioEncoder) Encode(frame av.AudioFrame) (pkts []av.Packet, err error) { +func (self *AudioEncoder) Encode(frame av.AudioFrame) (pkts [][]byte, err error) { var gotpkt bool - var pkt av.Packet + var pkt []byte if frame.SampleFormat != self.SampleFormat || frame.ChannelLayout != self.ChannelLayout || frame.SampleRate != self.SampleRate { if frame, err = self.resample(frame); err != nil {