add ffmpeg audio decoder and encoder
This commit is contained in:
parent
2eb65b8fac
commit
86e17d8e71
177
audio.go
Normal file
177
audio.go
Normal file
@ -0,0 +1,177 @@
|
||||
package codec
|
||||
|
||||
import (
|
||||
// #include "ffmpeg.h"
|
||||
"C"
|
||||
"unsafe"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
S16 = iota+1
|
||||
FLTP
|
||||
)
|
||||
|
||||
type AudioEncoder struct {
|
||||
ff C.FFCtx
|
||||
SampleRate int
|
||||
BitRate int
|
||||
ChannelCount int
|
||||
SampleFormat int
|
||||
sampleSize int
|
||||
}
|
||||
|
||||
func (self *AudioEncoder) Setup() (err error) {
|
||||
ff := &self.ff
|
||||
|
||||
switch self.SampleFormat {
|
||||
case S16:
|
||||
ff.codecCtx.sample_fmt = C.AV_SAMPLE_FMT_S16
|
||||
self.sampleSize = 2
|
||||
case FLTP:
|
||||
ff.codecCtx.sample_fmt = C.AV_SAMPLE_FMT_FLTP
|
||||
self.sampleSize = 4
|
||||
default:
|
||||
err = fmt.Errorf("unsupported sample format")
|
||||
return
|
||||
}
|
||||
|
||||
if self.BitRate == 0 {
|
||||
self.BitRate = 50000
|
||||
}
|
||||
|
||||
ff.frame = C.av_frame_alloc()
|
||||
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
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (self *AudioEncoder) Extradata() (data []byte) {
|
||||
data = make([]byte, (int)(self.ff.codecCtx.extradata_size))
|
||||
C.memcpy(
|
||||
unsafe.Pointer(&data[0]),
|
||||
unsafe.Pointer(self.ff.codecCtx.extradata),
|
||||
(C.size_t)(len(data)),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
func (self *AudioEncoder) Encode(sample []byte, flush bool) (gotPkt bool, pkt []byte, err error) {
|
||||
nbSamples := 1024
|
||||
expectedSize := nbSamples*self.sampleSize*self.ChannelCount
|
||||
|
||||
if len(sample) != expectedSize {
|
||||
err = fmt.Errorf("len(sample) should be %d", expectedSize)
|
||||
return
|
||||
}
|
||||
|
||||
frame := self.ff.frame
|
||||
frame.nb_samples = C.int(nbSamples)
|
||||
for i := 0; i < self.ChannelCount; i++ {
|
||||
frame.data[i] = (*C.uint8_t)(unsafe.Pointer(&sample[i*nbSamples*self.sampleSize]))
|
||||
frame.linesize[i] = C.int(nbSamples*self.sampleSize)
|
||||
}
|
||||
frame.extended_data = &frame.data[0]
|
||||
|
||||
cpkt := C.AVPacket{}
|
||||
cgotpkt := C.int(0)
|
||||
cerr := C.avcodec_encode_audio2(self.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 = make([]byte, (int)(cpkt.size))
|
||||
C.memcpy(
|
||||
unsafe.Pointer(&pkt[0]),
|
||||
unsafe.Pointer(cpkt.data),
|
||||
(C.size_t)(len(pkt)),
|
||||
)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type AudioDecoder struct {
|
||||
ff C.FFCtx
|
||||
Extradata []byte
|
||||
}
|
||||
|
||||
func (self *AudioDecoder) Setup() (err error) {
|
||||
ff := &self.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))
|
||||
}
|
||||
|
||||
if C.avcodec_open2(ff.codecCtx, ff.codec, nil) != 0 {
|
||||
err = fmt.Errorf("avcodec_open2 failed")
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (self *AudioDecoder) Decode(frame []byte) (gotPkt bool, pkt []byte, err error) {
|
||||
ff := &self.ff
|
||||
|
||||
cpkt := C.AVPacket{
|
||||
data: (*C.uint8_t)(unsafe.Pointer(&frame[0])),
|
||||
size: C.int(len(frame)),
|
||||
}
|
||||
cgotpkt := C.int(0)
|
||||
cerr := C.avcodec_decode_audio4(ff.codecCtx, ff.frame, &cgotpkt, &cpkt);
|
||||
if cerr < C.int(0) {
|
||||
err = fmt.Errorf("avcodec_decode_audio4 failed: %d", cerr)
|
||||
return
|
||||
}
|
||||
|
||||
if cgotpkt != C.int(0) {
|
||||
gotPkt = true
|
||||
pkt = make([]byte, (int)(cpkt.size))
|
||||
C.memcpy(
|
||||
unsafe.Pointer(&pkt[0]),
|
||||
unsafe.Pointer(cpkt.data),
|
||||
(C.size_t)(len(pkt)),
|
||||
)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func FindAudioEncoderByName(name string) (enc *AudioEncoder) {
|
||||
ff := C.FFCtx{}
|
||||
ff.codec = C.avcodec_find_encoder_by_name(C.CString(name))
|
||||
if ff.codec != nil {
|
||||
ff.codecCtx = C.avcodec_alloc_context3(ff.codec)
|
||||
if ff.codecCtx != nil {
|
||||
return &AudioEncoder{ff: ff}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func FindAudioDecoderByName(name string) (dec *AudioDecoder) {
|
||||
ff := C.FFCtx{}
|
||||
ff.codec = C.avcodec_find_decoder_by_name(C.CString(name))
|
||||
if ff.codec != nil {
|
||||
ff.codecCtx = C.avcodec_alloc_context3(ff.codec)
|
||||
if ff.codecCtx != nil {
|
||||
return &AudioDecoder{ff: ff}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
14
ffmpeg.h
Normal file
14
ffmpeg.h
Normal file
@ -0,0 +1,14 @@
|
||||
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavutil/avutil.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct {
|
||||
AVCodec *codec;
|
||||
AVCodecContext *codecCtx;
|
||||
AVFrame *frame;
|
||||
} FFCtx;
|
||||
|
||||
int FFCtxFindEncoderByName(FFCtx *ff, const char *name);
|
||||
int FFCtxFindDecoderByName(FFCtx *ff, const char *name);
|
||||
|
Loading…
x
Reference in New Issue
Block a user