diff --git a/README.md b/README.md deleted file mode 100644 index ac8920c..0000000 --- a/README.md +++ /dev/null @@ -1,77 +0,0 @@ - -codec -==== - -Golang aac/h264 encoder and decoder. - -H264 encoding example: - -```go -w := 400 -h := 400 -var nal [][]byte - -c, _ := codec.NewH264Encoder(w, h, image.YCbCrSubsampleRatio420) -nal = append(nal, c.Header) - -for i := 0; i < 60; i++ { - img := image.NewYCbCr(image.Rect(0,0,w,h), image.YCbCrSubsampleRatio420) - p, _ := c.Encode(img) - if len(p.Data) > 0 { - nal = append(nal, p.Data) - } -} -for { - // flush encoder - p, err := c.Encode(nil) - if err != nil { - break - } - nal = append(nal, p.Data) -} -``` - -H264 decoding example: - -```go -dec, err := codec.NewH264Decoder(nal[0]) -for i, n := range nal[1:] { - img, err := dec.Decode(n) - if err == nil { - fp, _ := os.Create(fmt.Sprintf("/tmp/dec-%d.jpg", i)) - jpeg.Encode(fp, img, nil) - fp.Close() - } -} -``` - -AAC encoding example: - -```go -var pkts [][]byte - -c, _ := codec.NewAACEncoder() -pkts = append(pkts, c.Header) - -for i := 0; i < 60; i++ { - var sample [8192]byte - p, _ := c.Encode(sample) - if len(p) > 0 { - pkts = append(pkts, p) - } -} -``` - -AAC decoding example: - -```go -dec, _ := codec.NewAACDecoder(pkts[0]) -for _, p := range pkts[1:] { - sample, err := dec.Decode(p) -} -``` - -License ----- - -All code is under WTFPL. You can use it for everything as you want :) diff --git a/aacdec.go b/aacdec.go deleted file mode 100644 index efa9653..0000000 --- a/aacdec.go +++ /dev/null @@ -1,82 +0,0 @@ -package codec - -import ( - /* - #include - #include - #include - #include - - typedef struct { - AVCodec *c; - AVCodecContext *ctx; - AVFrame *f; - int got; - } aacdec_t ; - - static int aacdec_new(aacdec_t *m, uint8_t *buf, int len) { - m->c = avcodec_find_decoder(CODEC_ID_AAC); - m->ctx = avcodec_alloc_context3(m->c); - m->f = av_frame_alloc(); - m->ctx->extradata = buf; - m->ctx->extradata_size = len; - m->ctx->debug = 0x3; - av_log(m->ctx, AV_LOG_DEBUG, "m %p\n", m); - return avcodec_open2(m->ctx, m->c, 0); - } - - static int aacdec_decode(aacdec_t *m, uint8_t *data, int len) { - AVPacket pkt; - av_init_packet(&pkt); - pkt.data = data; - pkt.size = len; - av_log(m->ctx, AV_LOG_DEBUG, "decode %p\n", m); - return avcodec_decode_audio4(m->ctx, m->f, &m->got, &pkt); - } - */ - "C" - "errors" - "unsafe" -) - -type AACDecoder struct { - m C.aacdec_t -} - -func NewAACDecoder(header []byte) (m *AACDecoder, err error) { - m = &AACDecoder{} - r := C.aacdec_new(&m.m, - (*C.uint8_t)(unsafe.Pointer(&header[0])), - (C.int)(len(header)), - ) - if int(r) < 0 { - err = errors.New("open codec failed") - } - return -} - -func (m *AACDecoder) Decode(data []byte) (sample []byte, err error) { - r := C.aacdec_decode( - &m.m, - (*C.uint8_t)(unsafe.Pointer(&data[0])), - (C.int)(len(data)), - ) - if int(r) < 0 { - err = errors.New("decode failed") - return - } - if int(m.m.got) == 0 { - err = errors.New("no data") - return - } - size := int(m.m.f.linesize[0]) * 2 - sample = make([]byte, size*2) - for i := 0; i < 2; i++ { - C.memcpy( - unsafe.Pointer(&sample[i*size]), - unsafe.Pointer(m.m.f.data[i]), - (C.size_t)(size), - ) - } - return -} diff --git a/aacenc.go b/aacenc.go deleted file mode 100644 index b350d1e..0000000 --- a/aacenc.go +++ /dev/null @@ -1,93 +0,0 @@ -package codec - -import ( - /* - #include - #include - #include - - typedef struct { - AVCodec *c; - AVCodecContext *ctx; - AVFrame *f; - int got; - uint8_t buf[1024*10]; int size; - int samplerate; int bitrate; - int channels; - } aacenc_t ; - - static int aacenc_new(aacenc_t *m) { - m->c = avcodec_find_encoder_by_name("aac"); - m->ctx = avcodec_alloc_context3(m->c); - m->ctx->sample_fmt = AV_SAMPLE_FMT_FLTP; - m->ctx->sample_rate = m->samplerate; - m->ctx->bit_rate = m->bitrate; - m->ctx->channels = m->channels; - m->ctx->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL; - m->f = av_frame_alloc(); - int r = avcodec_open2(m->ctx, m->c, 0); - //av_log(m->ctx, AV_LOG_DEBUG, "extra %d\n", m->ctx->extradata_size); - return r; - } - - static void aacenc_encode(aacenc_t *m) { - AVPacket pkt; - av_init_packet(&pkt); - pkt.data = m->buf; - pkt.size = sizeof(m->buf); - m->f->nb_samples = 1024; - m->f->extended_data = m->f->data; - m->f->linesize[0] = 1024*4; - //m->f->linesize[1] = 1024*4; - avcodec_encode_audio2(m->ctx, &pkt, m->f, &m->got); - av_log(m->ctx, AV_LOG_DEBUG, "got %d size %d\n", m->got, pkt.size); - m->size = pkt.size; - } - */ - "C" - "errors" - "unsafe" -) - -type AACEncoder struct { - m C.aacenc_t - Header []byte -} - -// only supported fltp,stereo,44100HZ. If you need other config, it's easy to modify code -func NewAACEncoder() (m *AACEncoder, err error) { - m = &AACEncoder{} - m.m.samplerate = 44100 - m.m.bitrate = 50000 - m.m.channels = 1 - r := C.aacenc_new(&m.m) - if int(r) != 0 { - err = errors.New("open codec failed") - return - } - m.Header = make([]byte, (int)(m.m.ctx.extradata_size)) - C.memcpy( - unsafe.Pointer(&m.Header[0]), - unsafe.Pointer(m.m.ctx.extradata), - (C.size_t)(len(m.Header)), - ) - return -} - -func (m *AACEncoder) Encode(sample []byte) (ret []byte, err error) { - m.m.f.data[0] = (*C.uint8_t)(unsafe.Pointer(&sample[0])) - //m.m.f.data[1] = (*C.uint8_t)(unsafe.Pointer(&sample[1024*4])) - C.aacenc_encode(&m.m) - if int(m.m.got) == 0 { - err = errors.New("no data") - return - } - ret = make([]byte, (int)(m.m.size)) - C.memcpy( - unsafe.Pointer(&ret[0]), - unsafe.Pointer(&m.m.buf[0]), - (C.size_t)(m.m.size), - ) - return -} - diff --git a/audio.go b/audio.go deleted file mode 100644 index 2456c38..0000000 --- a/audio.go +++ /dev/null @@ -1,189 +0,0 @@ -package codec - -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; - } - int wrap_av_get_bytes_per_sample(int sample_fmt) { - return av_get_bytes_per_sample(sample_fmt); - } - */ - "C" - "unsafe" - "fmt" -) - -type SampleFormat int - -func (self SampleFormat) BytesPerSample() int { - return int(C.wrap_av_get_bytes_per_sample(C.int(self))) -} - -const ( - S16 = SampleFormat(C.AV_SAMPLE_FMT_S16) - FLTP = SampleFormat(C.AV_SAMPLE_FMT_FLTP) -) - -type AudioEncoder struct { - ff C.FFCtx - SampleRate int - BitRate int - ChannelCount int - SampleFormat SampleFormat - FrameSampleCount int -} - -func (self *AudioEncoder) Setup() (err error) { - ff := &self.ff - - ff.frame = C.av_frame_alloc() - if self.BitRate == 0 { - self.BitRate = 50000 - } - 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 - } - self.SampleFormat = SampleFormat(int(ff.codecCtx.sample_fmt)) - self.ChannelCount = int(ff.codecCtx.channels) - self.FrameSampleCount = int(ff.codecCtx.frame_size) - - return -} - -func (self *AudioEncoder) Extradata() (data []byte) { - data = C.GoBytes(unsafe.Pointer(self.ff.codecCtx.extradata), self.ff.codecCtx.extradata_size) - return -} - -func (self *AudioEncoder) Encode(sample []byte, flush bool) (gotPkt bool, pkt []byte, err error) { - ff := &self.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(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 = C.GoBytes(unsafe.Pointer(cpkt.data), cpkt.size) - C.av_free_packet(&cpkt) - } - - return -} - -type AudioDecoder struct { - ff C.FFCtx - ChannelCount int - SampleFormat SampleFormat - 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)) - } - - ff.codecCtx.channels = C.int(self.ChannelCount) - if C.avcodec_open2(ff.codecCtx, ff.codec, nil) != 0 { - err = fmt.Errorf("avcodec_open2 failed") - return - } - self.SampleFormat = SampleFormat(int(ff.codecCtx.sample_fmt)) - self.ChannelCount = int(ff.codecCtx.channels) - - return -} - -func (self *AudioDecoder) Decode(frame []byte) (gotPkt bool, pkt []byte, err error) { - ff := &self.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 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 -} - diff --git a/ffmpeg.h b/ffmpeg.h deleted file mode 100644 index b336d17..0000000 --- a/ffmpeg.h +++ /dev/null @@ -1,11 +0,0 @@ - -#include -#include -#include - -typedef struct { - AVCodec *codec; - AVCodecContext *codecCtx; - AVFrame *frame; -} FFCtx; - diff --git a/h264dec.go b/h264dec.go deleted file mode 100644 index 747051a..0000000 --- a/h264dec.go +++ /dev/null @@ -1,88 +0,0 @@ -package codec - -import ( - /* - #include - #include - #include - - typedef struct { - AVCodec *c; - AVCodecContext *ctx; - AVFrame *f; - int got; - } h264dec_t ; - - static int h264dec_new(h264dec_t *h, uint8_t *data, int len) { - h->c = avcodec_find_decoder(CODEC_ID_H264); - h->ctx = avcodec_alloc_context3(h->c); - h->f = av_frame_alloc(); - h->ctx->extradata = data; - h->ctx->extradata_size = len; - h->ctx->debug = 0x3; - return avcodec_open2(h->ctx, h->c, 0); - } - - static int h264dec_decode(h264dec_t *h, uint8_t *data, int len) { - AVPacket pkt; - av_init_packet(&pkt); - pkt.data = data; - pkt.size = len; - return avcodec_decode_video2(h->ctx, h->f, &h->got, &pkt); - } - */ - "C" - "errors" - "image" - "unsafe" -) - -type H264Decoder struct { - m C.h264dec_t -} - -func NewH264Decoder(header []byte) (m *H264Decoder, err error) { - m = &H264Decoder{} - r := C.h264dec_new( - &m.m, - (*C.uint8_t)(unsafe.Pointer(&header[0])), - (C.int)(len(header)), - ) - if int(r) < 0 { - err = errors.New("open codec failed") - } - return -} - -func (m *H264Decoder) Decode(nal []byte) (f *image.YCbCr, err error) { - r := C.h264dec_decode( - &m.m, - (*C.uint8_t)(unsafe.Pointer(&nal[0])), - (C.int)(len(nal)), - ) - if int(r) < 0 { - err = errors.New("decode failed") - return - } - if m.m.got == 0 { - err = errors.New("no picture") - return - } - - w := int(m.m.f.width) - h := int(m.m.f.height) - ys := int(m.m.f.linesize[0]) - cs := int(m.m.f.linesize[1]) - - f = &image.YCbCr{ - Y: fromCPtr(unsafe.Pointer(m.m.f.data[0]), ys*h), - Cb: fromCPtr(unsafe.Pointer(m.m.f.data[1]), cs*h/2), - Cr: fromCPtr(unsafe.Pointer(m.m.f.data[2]), cs*h/2), - YStride: ys, - CStride: cs, - SubsampleRatio: image.YCbCrSubsampleRatio420, - Rect: image.Rect(0, 0, w, h), - } - - return -} diff --git a/h264enc.go b/h264enc.go deleted file mode 100644 index 21bdb9f..0000000 --- a/h264enc.go +++ /dev/null @@ -1,146 +0,0 @@ -package codec - -import ( - - /* - #include - #include - #include - #include - #include - #include - #include - - typedef struct { - int w, h; - int pixfmt; - char *preset[2]; - char *profile; - int bitrate; - int got; - AVCodec *c; - AVCodecContext *ctx; - AVFrame *f; - AVPacket pkt; - } h264enc_t; - - static int h264enc_new(h264enc_t *m) { - m->c = avcodec_find_encoder(CODEC_ID_H264); - m->ctx = avcodec_alloc_context3(m->c); - m->ctx->width = m->w; - m->ctx->height = m->h; - m->ctx->bit_rate = m->bitrate; - m->ctx->pix_fmt = m->pixfmt; - m->ctx->flags |= CODEC_FLAG_GLOBAL_HEADER; - m->f = av_frame_alloc(); - return avcodec_open2(m->ctx, m->c, NULL); - } - - */ - "C" - "errors" - "image" - "strings" - "unsafe" - //"log" -) - -type H264Encoder struct { - m C.h264enc_t - Header []byte - Pixfmt image.YCbCrSubsampleRatio - W, H int -} - -func NewH264Encoder( - w, h int, - pixfmt image.YCbCrSubsampleRatio, - opts ...string, -) (m *H264Encoder, err error) { - m = &H264Encoder{} - m.m.w = (C.int)(w) - m.m.h = (C.int)(h) - m.W = w - m.H = h - m.Pixfmt = pixfmt - switch pixfmt { - case image.YCbCrSubsampleRatio444: - m.m.pixfmt = C.PIX_FMT_YUV444P - case image.YCbCrSubsampleRatio422: - m.m.pixfmt = C.PIX_FMT_YUV422P - case image.YCbCrSubsampleRatio420: - m.m.pixfmt = C.PIX_FMT_YUV420P - } - for _, opt := range opts { - a := strings.Split(opt, ",") - switch { - case a[0] == "preset" && len(a) == 3: - m.m.preset[0] = C.CString(a[1]) - m.m.preset[1] = C.CString(a[2]) - case a[0] == "profile" && len(a) == 2: - m.m.profile = C.CString(a[1]) - } - } - r := C.h264enc_new(&m.m) - if int(r) < 0 { - err = errors.New("open encoder failed") - return - } - m.Header = fromCPtr(unsafe.Pointer(m.m.ctx.extradata), (int)(m.m.ctx.extradata_size)) - //m.Header = fromCPtr(unsafe.Pointer(m.m.pps), (int)(m.m.ppslen)) - return -} - -type h264Out struct { - Data []byte - Key bool -} - -func (m *H264Encoder) Encode(img *image.YCbCr) (out h264Out, err error) { - var f *C.AVFrame - if img == nil { - f = nil - } else { - if img.SubsampleRatio != m.Pixfmt { - err = errors.New("image pixfmt not match") - return - } - if img.Rect.Dx() != m.W || img.Rect.Dy() != m.H { - err = errors.New("image size not match") - return - } - f = m.m.f - f.data[0] = (*C.uint8_t)(unsafe.Pointer(&img.Y[0])) - f.data[1] = (*C.uint8_t)(unsafe.Pointer(&img.Cb[0])) - f.data[2] = (*C.uint8_t)(unsafe.Pointer(&img.Cr[0])) - f.linesize[0] = (C.int)(img.YStride) - f.linesize[1] = (C.int)(img.CStride) - f.linesize[2] = (C.int)(img.CStride) - } - - C.av_init_packet(&m.m.pkt) - r := C.avcodec_encode_video2(m.m.ctx, &m.m.pkt, f, &m.m.got) - defer C.av_free_packet(&m.m.pkt) - if int(r) < 0 { - err = errors.New("encode failed") - return - } - if m.m.got == 0 { - err = errors.New("no picture") - return - } - if m.m.pkt.size == 0 { - err = errors.New("packet size == 0") - return - } - - out.Data = make([]byte, m.m.pkt.size) - C.memcpy( - unsafe.Pointer(&out.Data[0]), - unsafe.Pointer(m.m.pkt.data), - (C.size_t)(m.m.pkt.size), - ) - out.Key = (m.m.pkt.flags & C.AV_PKT_FLAG_KEY) != 0 - - return -} diff --git a/util.go b/util.go deleted file mode 100644 index 58ca3a7..0000000 --- a/util.go +++ /dev/null @@ -1,48 +0,0 @@ -/* - -Golang h264,aac decoder/encoder libav wrapper - - d, err = codec.NewAACEncoder() - data, err = d.Encode(samples) - - d, err = codec.NewAACDecoder(aaccfg) - samples, err = d.Decode(data) - - var img *image.YCbCr - d, err = codec.NewH264Encoder(640, 480) - img, err = d.Encode(img) - - d, err = codec.NewH264Decoder(pps) - img, err = d.Decode(nal) -*/ -package codec - -import ( - "reflect" - "unsafe" - - /* - #cgo LDFLAGS: -lavformat -lavutil -lavcodec - - #include - #include - - static void libav_init() { - av_register_all(); - av_log_set_level(AV_LOG_DEBUG); - } - */ - "C" -) - -func init() { - C.libav_init() -} - -func fromCPtr(buf unsafe.Pointer, size int) (ret []uint8) { - hdr := (*reflect.SliceHeader)((unsafe.Pointer(&ret))) - hdr.Cap = size - hdr.Len = size - hdr.Data = uintptr(buf) - return -}