168 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			168 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package ffmpeg
 | |
| 
 | |
| /*
 | |
| #include "ffmpeg.h"
 | |
| int wrap_avcodec_decode_video2(AVCodecContext *ctx, AVFrame *frame, void *data, int size, int *got) {
 | |
| 	struct AVPacket pkt = {.data = data, .size = size};
 | |
| 	return avcodec_decode_video2(ctx, frame, got, &pkt);
 | |
| }
 | |
| 
 | |
| int wrap_avcodec_decode_video2_empty(AVCodecContext *ctx, AVFrame *frame, void *data, int size, int *got) {
 | |
| 	struct AVPacket pkt = {.data = NULL, .size = 0};
 | |
| 	return avcodec_decode_video2(ctx, frame, got, &pkt);
 | |
| }
 | |
| */
 | |
| import "C"
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"image"
 | |
| 	"reflect"
 | |
| 	"runtime"
 | |
| 	"unsafe"
 | |
| 
 | |
| 	"git.r-2.top/kunmeng/vdk/av"
 | |
| 	"git.r-2.top/kunmeng/vdk/codec/h264parser"
 | |
| )
 | |
| 
 | |
| type VideoDecoder struct {
 | |
| 	ff        *ffctx
 | |
| 	Extradata []byte
 | |
| }
 | |
| 
 | |
| func (self *VideoDecoder) Setup() (err error) {
 | |
| 	ff := &self.ff.ff
 | |
| 	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("ffmpeg: decoder: avcodec_open2 failed")
 | |
| 		return
 | |
| 	}
 | |
| 	return
 | |
| }
 | |
| 
 | |
| 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
 | |
| }
 | |
| 
 | |
| type VideoFrame struct {
 | |
| 	Image image.YCbCr
 | |
| 	frame *C.AVFrame
 | |
| }
 | |
| 
 | |
| func (self *VideoFrame) Free() {
 | |
| 	self.Image = image.YCbCr{}
 | |
| 	C.av_frame_free(&self.frame)
 | |
| }
 | |
| 
 | |
| func freeVideoFrame(self *VideoFrame) {
 | |
| 	self.Free()
 | |
| }
 | |
| 
 | |
| func (self *VideoDecoder) Decode(pkt []byte) (img *VideoFrame, err error) {
 | |
| 	ff := &self.ff.ff
 | |
| 
 | |
| 	cgotimg := C.int(0)
 | |
| 	frame := C.av_frame_alloc()
 | |
| 	cerr := C.wrap_avcodec_decode_video2(ff.codecCtx, frame, unsafe.Pointer(&pkt[0]), C.int(len(pkt)), &cgotimg)
 | |
| 	if cerr < C.int(0) {
 | |
| 		err = fmt.Errorf("ffmpeg: avcodec_decode_video2 failed: %d", cerr)
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	if cgotimg != C.int(0) {
 | |
| 		w := int(frame.width)
 | |
| 		h := int(frame.height)
 | |
| 		ys := int(frame.linesize[0])
 | |
| 		cs := int(frame.linesize[1])
 | |
| 
 | |
| 		img = &VideoFrame{Image: image.YCbCr{
 | |
| 			Y:              fromCPtr(unsafe.Pointer(frame.data[0]), ys*h),
 | |
| 			Cb:             fromCPtr(unsafe.Pointer(frame.data[1]), cs*h/2),
 | |
| 			Cr:             fromCPtr(unsafe.Pointer(frame.data[2]), cs*h/2),
 | |
| 			YStride:        ys,
 | |
| 			CStride:        cs,
 | |
| 			SubsampleRatio: image.YCbCrSubsampleRatio420,
 | |
| 			Rect:           image.Rect(0, 0, w, h),
 | |
| 		}, frame: frame}
 | |
| 		runtime.SetFinalizer(img, freeVideoFrame)
 | |
| 	}
 | |
| 
 | |
| 	return
 | |
| }
 | |
| 
 | |
| func (self *VideoDecoder) DecodeSingle(pkt []byte) (img *VideoFrame, err error) {
 | |
| 	ff := &self.ff.ff
 | |
| 	cgotimg := C.int(0)
 | |
| 	frame := C.av_frame_alloc()
 | |
| 	cerr := C.wrap_avcodec_decode_video2(ff.codecCtx, frame, unsafe.Pointer(&pkt[0]), C.int(len(pkt)), &cgotimg)
 | |
| 	if cerr < C.int(0) {
 | |
| 		err = fmt.Errorf("ffmpeg: avcodec_decode_video2 failed: %d", cerr)
 | |
| 		return
 | |
| 	}
 | |
| 	//https://stackoverflow.com/questions/25431413/decode-compressed-frame-to-memory-using-libav-avcodec-decode-video2
 | |
| 	if cgotimg == C.int(0) {
 | |
| 		cerr = C.wrap_avcodec_decode_video2_empty(ff.codecCtx, frame, unsafe.Pointer(&pkt[0]), C.int(0), &cgotimg)
 | |
| 		if cerr < C.int(0) {
 | |
| 			err = fmt.Errorf("ffmpeg: avcodec_decode_video2 failed: %d", cerr)
 | |
| 			return
 | |
| 		}
 | |
| 	}
 | |
| 	if cgotimg != C.int(0) {
 | |
| 		w := int(frame.width)
 | |
| 		h := int(frame.height)
 | |
| 		ys := int(frame.linesize[0])
 | |
| 		cs := int(frame.linesize[1])
 | |
| 
 | |
| 		img = &VideoFrame{Image: image.YCbCr{
 | |
| 			Y:              fromCPtr(unsafe.Pointer(frame.data[0]), ys*h),
 | |
| 			Cb:             fromCPtr(unsafe.Pointer(frame.data[1]), cs*h/2),
 | |
| 			Cr:             fromCPtr(unsafe.Pointer(frame.data[2]), cs*h/2),
 | |
| 			YStride:        ys,
 | |
| 			CStride:        cs,
 | |
| 			SubsampleRatio: image.YCbCrSubsampleRatio420,
 | |
| 			Rect:           image.Rect(0, 0, w, h),
 | |
| 		}, frame: frame}
 | |
| 		runtime.SetFinalizer(img, freeVideoFrame)
 | |
| 	}
 | |
| 
 | |
| 	return
 | |
| }
 | |
| 
 | |
| func NewVideoDecoder(stream av.CodecData) (dec *VideoDecoder, err error) {
 | |
| 	_dec := &VideoDecoder{}
 | |
| 	var id uint32
 | |
| 
 | |
| 	switch stream.Type() {
 | |
| 	case av.H264:
 | |
| 		h264 := stream.(h264parser.CodecData)
 | |
| 		_dec.Extradata = h264.AVCDecoderConfRecordBytes()
 | |
| 		id = C.AV_CODEC_ID_H264
 | |
| 
 | |
| 	default:
 | |
| 		err = fmt.Errorf("ffmpeg: NewVideoDecoder codec=%v unsupported", stream.Type())
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	c := C.avcodec_find_decoder(id)
 | |
| 	if c == nil || C.avcodec_get_type(id) != C.AVMEDIA_TYPE_VIDEO {
 | |
| 		err = fmt.Errorf("ffmpeg: cannot find video decoder codecId=%d", id)
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	if _dec.ff, err = newFFCtxByCodec(c); err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 	if err = _dec.Setup(); err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	dec = _dec
 | |
| 	return
 | |
| }
 | 
