From 3ce06bb9a1390f11ce319c9959e4e97ad59a7d79 Mon Sep 17 00:00:00 2001 From: cfanfrank Date: Fri, 1 Mar 2013 20:01:14 +0800 Subject: [PATCH] add codec --- codec/aacdec.go | 81 +++++++++++++++++++++++++++++++ codec/aacenc.go | 83 ++++++++++++++++++++++++++++++++ codec/h264dec.go | 88 +++++++++++++++++++++++++++++++++ codec/h264enc.go | 123 +++++++++++++++++++++++++++++++++++++++++++++++ codec/util.go | 37 ++++++++++++++ 5 files changed, 412 insertions(+) create mode 100644 codec/aacdec.go create mode 100644 codec/aacenc.go create mode 100644 codec/h264dec.go create mode 100644 codec/h264enc.go create mode 100644 codec/util.go diff --git a/codec/aacdec.go b/codec/aacdec.go new file mode 100644 index 0000000..66cc9f4 --- /dev/null +++ b/codec/aacdec.go @@ -0,0 +1,81 @@ + +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 = avcodec_alloc_frame(); + 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 void 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); + avcodec_decode_audio4(m->ctx, m->f, &m->got, &pkt); + av_log(m->ctx, AV_LOG_DEBUG, "got %d\n", m->got); + } + */ + "C" + "unsafe" + "errors" +) + +type AACDecoder struct { + m C.aacdec_t +} + +func NewAACDecoder(cfg []byte) (m *AACDecoder, err error) { + m = &AACDecoder{} + r := C.aacdec_new( + &m.m, + (*C.uint8_t)(unsafe.Pointer(&cfg[0])), + (C.int)(len(cfg)), + ) + if int(r) != 0 { + err = errors.New("avcodec open failed") + } + return +} + +func (m *AACDecoder) Decode(data []byte) (sample []byte, err error) { + C.aacdec_decode( + &m.m, + (*C.uint8_t)(unsafe.Pointer(&data[0])), + (C.int)(len(data)), + ) + if int(m.m.got) == 0 { + err = errors.New("no data") + return + } + sample = make([]byte, 8192) + for i := 0; i < 2; i++ { + C.memcpy( + unsafe.Pointer(&sample[i*4096]), + unsafe.Pointer(m.m.f.data[i]), + (C.size_t)(4096), + ) + } + return +} + diff --git a/codec/aacenc.go b/codec/aacenc.go new file mode 100644 index 0000000..d205ee0 --- /dev/null +++ b/codec/aacenc.go @@ -0,0 +1,83 @@ + +package codec + +import ( + /* + #include + #include + #include + + typedef struct { + AVCodec *c; + AVCodecContext *ctx; + AVFrame *f; + int got; + uint8_t buf[1024*10]; int size; + } aacenc_t ; + + static void aacenc_new(aacenc_t *m) { + m->c = avcodec_find_encoder(CODEC_ID_AAC); + m->ctx = avcodec_alloc_context3(m->c); + m->ctx->sample_fmt = AV_SAMPLE_FMT_FLTP; + m->ctx->sample_rate = 44100; + m->ctx->bit_rate = 100000; + m->ctx->channels = 2; + m->ctx->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL; + m->f = avcodec_alloc_frame(); + avcodec_open2(m->ctx, m->c, 0); + av_log(m->ctx, AV_LOG_DEBUG, "extra %d\n", m->ctx->extradata_size); + } + + 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] = 4096; + 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" + "unsafe" + "errors" +) + +type AACEncoder struct { + m C.aacenc_t + Cfg []byte // AAC Audio config +} + +// only supported fltp,stereo,44100khz. If you need other config, it's easy to modify code +func NewAACEncoder() (m *AACEncoder) { + m = &AACEncoder{} + C.aacenc_new(&m.m) + m.Cfg = make([]byte, (int)(m.m.ctx.extradata_size)) + C.memcpy( + unsafe.Pointer(&m.Cfg[0]), + unsafe.Pointer(&m.m.ctx.extradata), + (C.size_t)(len(m.Cfg)), + ) + 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[4096])) + 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/codec/h264dec.go b/codec/h264dec.go new file mode 100644 index 0000000..16bd3d4 --- /dev/null +++ b/codec/h264dec.go @@ -0,0 +1,88 @@ + +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 = avcodec_alloc_frame(); + h->ctx->extradata = data; + h->ctx->extradata_size = len; + h->ctx->debug = 0x3; + return avcodec_open2(h->ctx, h->c, 0); + } + + static void h264dec_decode(h264dec_t *h, uint8_t *data, int len) { + AVPacket pkt; + av_init_packet(&pkt); + pkt.data = data; + pkt.size = len; + avcodec_decode_video2(h->ctx, h->f, &h->got, &pkt); + av_log(h->ctx, AV_LOG_DEBUG, "got %d\n", h->got); + } + */ + "C" + "unsafe" + "errors" + "image" +) + +type H264Decoder struct { + m C.h264dec_t +} + +func NewH264Decoder(pps []byte) (m *H264Decoder, err error) { + m = &H264Decoder{} + r := C.h264dec_new( + &m.m, + (*C.uint8_t)(unsafe.Pointer(&pps[0])), + (C.int)(len(pps)), + ) + if int(r) != 0 { + m = nil + err = errors.New("open codec failed") + } + return +} + +func (m *H264Decoder) Decode(nal []byte) (f *image.YCbCr, err error) { + C.h264dec_decode( + &m.m, + (*C.uint8_t)(unsafe.Pointer(&nal[0])), + (C.int)(len(nal)), + ) + 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/codec/h264enc.go b/codec/h264enc.go new file mode 100644 index 0000000..df821fb --- /dev/null +++ b/codec/h264enc.go @@ -0,0 +1,123 @@ + +package codec + +import ( + +/* +#include +#include +#include +#include +#include + +typedef struct { + x264_t *x; + int w, h; + uint8_t *pps; int ppslen; + uint8_t *sei; int seilen; + x264_picture_t in; + x264_nal_t *nal; int nnal; int nallen; +} h264enc_t; + +static void h264enc_new(h264enc_t *m) { + x264_param_t p; + + x264_param_default(&p); + x264_param_default_preset(&p, "ultrafast", "zerolatency"); + x264_param_apply_profile(&p, "main"); + + p.rc.i_bitrate = 50; + p.rc.i_rc_method = X264_RC_ABR; + p.i_width = m->w; + p.i_height = m->h; + p.i_csp = X264_CSP_I420; + + m->x = x264_encoder_open(&p); + if (!m->x) { + return ; + } + + x264_nal_t *nal; + int nnal, i; + + uint8_t *pps; + m->ppslen = x264_encoder_headers(m->x, &nal, &nnal); + m->pps = pps = malloc(m->ppslen); + + for (i = 0; i < nnal; i++) { + //printf("nal#%d %d\n", i, nal[i].i_type); + if (nal[i].i_type != NAL_SEI) { + memcpy(pps, nal[i].p_payload, nal[i].i_payload); + pps += nal[i].i_payload; + } else { + m->seilen = nal[i].i_payload; + m->sei = malloc(m->seilen); + memcpy(m->sei, nal[i].p_payload, m->seilen); + } + } + m->ppslen = pps - m->pps; +} + +static void h264enc_encode(h264enc_t *m) { + x264_picture_t out; + + m->in.img.i_csp = X264_CSP_I420; + m->in.i_type = X264_TYPE_AUTO; + + x264_encoder_encode(m->x, &m->nal, &m->nnal, &m->in, &out); + m->nallen = 0; + int i; + if (m->seilen) + m->nallen += m->seilen; + for (i = 0; i < m->nnal; i++) { + m->nallen += m->nal[i].i_payload; + } +} + +static void h264enc_copy(h264enc_t *m, uint8_t *p) { + int i; + if (m->seilen) { + memcpy(p, m->sei, m->seilen); + p += m->seilen; + m->seilen = 0; + } + for (i = 0; i < m->nnal; i++) { + memcpy(p, m->nal[i].p_payload, m->nal[i].i_payload); + p += m->nal[i].i_payload; + } +} + +*/ + "C" + "unsafe" + "image" +) + +type H264Encoder struct { + m C.h264enc_t + PPS []byte +} + +func NewH264Encoder(w, h int) (m *H264Encoder) { + m = &H264Encoder{} + m.m.w = (C.int)(w) + m.m.h = (C.int)(h) + C.h264enc_new(&m.m) + m.PPS = fromCPtr(unsafe.Pointer(m.m.pps), (int)(m.m.ppslen)) + return +} + +func (m *H264Encoder) Encode(f *image.YCbCr) (nal []byte) { + C.x264_picture_init(&m.m.in); + m.m.in.img.plane[0] = (*C.uint8_t)(unsafe.Pointer(&f.Y[0])); + m.m.in.img.plane[1] = (*C.uint8_t)(unsafe.Pointer(&f.Cb[0])); + m.m.in.img.plane[2] = (*C.uint8_t)(unsafe.Pointer(&f.Cr[0])); + m.m.in.img.i_stride[0] = (C.int)(f.YStride); + m.m.in.img.i_stride[1] = (C.int)(f.CStride); + m.m.in.img.i_stride[2] = (C.int)(f.CStride); + C.h264enc_encode(&m.m) + nal = make([]byte, m.m.nallen) + C.h264enc_copy(&m.m, (*C.uint8_t)(unsafe.Pointer(&nal[0]))) + return +} + diff --git a/codec/util.go b/codec/util.go new file mode 100644 index 0000000..0a7806d --- /dev/null +++ b/codec/util.go @@ -0,0 +1,37 @@ + +/* + Golang h264,aac decoder/encoder libav wrapper +*/ +package codec + +import ( + "unsafe" + "reflect" + + /* + #cgo darwin LDFLAGS: -lavformat -lavutil -lavcodec -lx264 + + #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 +} + +