mirror of
https://github.com/3d0c/gmf
synced 2025-12-24 10:40:59 +08:00
fixes for go1.12 support
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
// +build go1.11,!go1.12
|
||||
|
||||
package gmf
|
||||
|
||||
/*
|
||||
180
avio_go112.go
Normal file
180
avio_go112.go
Normal file
@@ -0,0 +1,180 @@
|
||||
// +build go1.12
|
||||
|
||||
package gmf
|
||||
|
||||
/*
|
||||
|
||||
#cgo pkg-config: libavformat
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "libavformat/avio.h"
|
||||
#include "libavformat/avformat.h"
|
||||
|
||||
extern int readCallBack(void*, uint8_t*, int);
|
||||
extern int writeCallBack(void*, uint8_t*, int);
|
||||
extern int64_t seekCallBack(void*, int64_t, int);
|
||||
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
AVIO_FLAG_READ = 1
|
||||
AVIO_FLAG_WRITE = 2
|
||||
AVIO_FLAG_READ_WRITE = (AVIO_FLAG_READ | AVIO_FLAG_WRITE)
|
||||
)
|
||||
|
||||
var (
|
||||
IO_BUFFER_SIZE int = 32768
|
||||
)
|
||||
|
||||
// Functions prototypes for custom IO. Implement necessary prototypes and pass instance pointer to NewAVIOContext.
|
||||
//
|
||||
// E.g.:
|
||||
// func gridFsReader() ([]byte, int) {
|
||||
// ... implementation ...
|
||||
// return data, length
|
||||
// }
|
||||
//
|
||||
// avoictx := NewAVIOContext(ctx, &AVIOHandlers{ReadPacket: gridFsReader})
|
||||
type AVIOHandlers struct {
|
||||
ReadPacket func() ([]byte, int)
|
||||
WritePacket func([]byte) int
|
||||
Seek func(int64, int) int64
|
||||
}
|
||||
|
||||
// Global map of AVIOHandlers
|
||||
// one handlers struct per format context. Using ctx.avCtx pointer address as a key.
|
||||
var handlersMap map[uintptr]*AVIOHandlers
|
||||
|
||||
type AVIOContext struct {
|
||||
avAVIOContext *C.AVIOContext
|
||||
// avAVIOContext *C.struct_AVIOContext
|
||||
handlerKey uintptr
|
||||
CgoMemoryManage
|
||||
buffer *C.uchar
|
||||
}
|
||||
|
||||
// AVIOContext constructor. Use it only if You need custom IO behaviour!
|
||||
func NewAVIOContext(ctx *FmtCtx, handlers *AVIOHandlers, size ...int) (*AVIOContext, error) {
|
||||
this := &AVIOContext{}
|
||||
|
||||
bufferSize := IO_BUFFER_SIZE
|
||||
|
||||
if len(size) == 1 {
|
||||
bufferSize = size[0]
|
||||
}
|
||||
|
||||
this.buffer = (*C.uchar)(C.av_malloc(C.size_t(bufferSize)))
|
||||
|
||||
if this.buffer == nil {
|
||||
return nil, errors.New("unable to allocate buffer")
|
||||
}
|
||||
|
||||
// we have to explicitly set it to nil, to force library using default handlers
|
||||
var ptrRead, ptrWrite, ptrSeek *[0]byte = nil, nil, nil
|
||||
|
||||
if handlers != nil {
|
||||
if handlersMap == nil {
|
||||
handlersMap = make(map[uintptr]*AVIOHandlers)
|
||||
}
|
||||
|
||||
handlersMap[uintptr(unsafe.Pointer(ctx.avCtx))] = handlers
|
||||
this.handlerKey = uintptr(unsafe.Pointer(ctx.avCtx))
|
||||
}
|
||||
|
||||
var flag int = 0
|
||||
|
||||
if handlers.ReadPacket != nil {
|
||||
ptrRead = (*[0]byte)(C.readCallBack)
|
||||
flag = 0
|
||||
}
|
||||
|
||||
if handlers.WritePacket != nil {
|
||||
ptrWrite = (*[0]byte)(C.writeCallBack)
|
||||
flag = AVIO_FLAG_WRITE
|
||||
}
|
||||
|
||||
if handlers.Seek != nil {
|
||||
ptrSeek = (*[0]byte)(C.seekCallBack)
|
||||
}
|
||||
|
||||
if handlers.ReadPacket != nil && handlers.WritePacket != nil {
|
||||
flag = AVIO_FLAG_READ_WRITE
|
||||
}
|
||||
|
||||
if this.avAVIOContext = C.avio_alloc_context(this.buffer, C.int(bufferSize), C.int(flag), unsafe.Pointer(ctx.avCtx), ptrRead, ptrWrite, ptrSeek); this.avAVIOContext == nil {
|
||||
C.av_free(unsafe.Pointer(this.avAVIOContext.buffer))
|
||||
return nil, errors.New("unable to initialize avio context")
|
||||
}
|
||||
|
||||
this.avAVIOContext.min_packet_size = C.int(bufferSize)
|
||||
|
||||
return this, nil
|
||||
}
|
||||
|
||||
func (this *AVIOContext) Free() {
|
||||
delete(handlersMap, this.handlerKey)
|
||||
C.av_free(unsafe.Pointer(this.avAVIOContext.buffer))
|
||||
C.av_free(unsafe.Pointer(this.avAVIOContext))
|
||||
}
|
||||
|
||||
func (this *AVIOContext) Flush() {
|
||||
C.avio_flush(this.avAVIOContext)
|
||||
}
|
||||
|
||||
//export readCallBack
|
||||
func readCallBack(opaque unsafe.Pointer, buf *C.uint8_t, buf_size C.int) C.int {
|
||||
handlers, found := handlersMap[uintptr(opaque)]
|
||||
if !found {
|
||||
panic(fmt.Sprintf("No handlers instance found, according pointer: %v", opaque))
|
||||
}
|
||||
|
||||
if handlers.ReadPacket == nil {
|
||||
panic("No reader handler initialized")
|
||||
}
|
||||
|
||||
b, n := handlers.ReadPacket()
|
||||
if n > 0 {
|
||||
C.memcpy(unsafe.Pointer(buf), unsafe.Pointer(&b[0]), C.size_t(n))
|
||||
}
|
||||
|
||||
return C.int(n)
|
||||
}
|
||||
|
||||
//export writeCallBack
|
||||
func writeCallBack(opaque unsafe.Pointer, buf *C.uint8_t, buf_size C.int) C.int {
|
||||
handlers, found := handlersMap[uintptr(opaque)]
|
||||
if !found {
|
||||
panic(fmt.Sprintf("No handlers instance found, according pointer: %v", opaque))
|
||||
}
|
||||
|
||||
if handlers.WritePacket == nil {
|
||||
panic("No writer handler initialized.")
|
||||
}
|
||||
|
||||
return C.int(handlers.WritePacket(C.GoBytes(unsafe.Pointer(buf), buf_size)))
|
||||
}
|
||||
|
||||
//export seekCallBack
|
||||
func seekCallBack(opaque unsafe.Pointer, offset C.int64_t, whence C.int) C.int64_t {
|
||||
handlers, found := handlersMap[uintptr(opaque)]
|
||||
if !found {
|
||||
panic(fmt.Sprintf("No handlers instance found, according pointer: %v", opaque))
|
||||
}
|
||||
|
||||
if handlers.Seek == nil {
|
||||
panic("No seek handler initialized.")
|
||||
}
|
||||
|
||||
return C.int64_t(handlers.Seek(int64(offset), int(whence)))
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
// +build go1.11,!go1.12
|
||||
|
||||
package gmf
|
||||
|
||||
/*
|
||||
@@ -193,7 +195,7 @@ func (cc *CodecCtx) CopyExtra(ist *Stream) *CodecCtx {
|
||||
|
||||
codec.field_order = icodec.field_order
|
||||
|
||||
codec.extradata = (*_Ctype_uint8_t)(C.av_mallocz((_Ctype_size_t)((C.uint64_t)(icodec.extradata_size) + C.AV_INPUT_BUFFER_PADDING_SIZE)))
|
||||
codec.extradata = (*Ctype_uint8_t)(C.av_mallocz((_Ctype_size_t)((C.uint64_t)(icodec.extradata_size) + C.AV_INPUT_BUFFER_PADDING_SIZE)))
|
||||
|
||||
C.memcpy(unsafe.Pointer(codec.extradata), unsafe.Pointer(icodec.extradata), (_Ctype_size_t)(icodec.extradata_size))
|
||||
codec.extradata_size = icodec.extradata_size
|
||||
608
codecCtx_go112.go
Normal file
608
codecCtx_go112.go
Normal file
@@ -0,0 +1,608 @@
|
||||
// +build go1.12
|
||||
|
||||
package gmf
|
||||
|
||||
/*
|
||||
|
||||
#cgo pkg-config: libavcodec libavutil
|
||||
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "libavcodec/avcodec.h"
|
||||
#include "libavutil/channel_layout.h"
|
||||
#include "libavutil/samplefmt.h"
|
||||
#include "libavutil/opt.h"
|
||||
#include "libavutil/mem.h"
|
||||
#include "libavutil/bprint.h"
|
||||
|
||||
static int check_sample_fmt(AVCodec *codec, enum AVSampleFormat sample_fmt) {
|
||||
const enum AVSampleFormat *p = codec->sample_fmts;
|
||||
|
||||
while (*p != AV_SAMPLE_FMT_NONE) {
|
||||
if (*p == sample_fmt)
|
||||
return 1;
|
||||
p++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int select_sample_rate(AVCodec *codec) {
|
||||
const int *p;
|
||||
int best_samplerate = 0;
|
||||
|
||||
if (!codec->supported_samplerates)
|
||||
return 44100;
|
||||
|
||||
p = codec->supported_samplerates;
|
||||
while (*p) {
|
||||
best_samplerate = FFMAX(*p, best_samplerate);
|
||||
p++;
|
||||
}
|
||||
return best_samplerate;
|
||||
}
|
||||
|
||||
static int select_channel_layout(AVCodec *codec) {
|
||||
const uint64_t *p;
|
||||
uint64_t best_ch_layout = 0;
|
||||
int best_nb_channels = 0;
|
||||
|
||||
if (!codec->channel_layouts)
|
||||
return AV_CH_LAYOUT_STEREO;
|
||||
|
||||
p = codec->channel_layouts;
|
||||
while (*p) {
|
||||
int nb_channels = av_get_channel_layout_nb_channels(*p);
|
||||
|
||||
if (nb_channels > best_nb_channels) {
|
||||
best_ch_layout = *p;
|
||||
best_nb_channels = nb_channels;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
return best_ch_layout;
|
||||
}
|
||||
|
||||
static void call_av_freep(AVCodecContext *out){
|
||||
return av_freep(&out);
|
||||
}
|
||||
|
||||
static char * gmf_get_channel_layout_name(int channels, int layout) {
|
||||
AVBPrint pbuf;
|
||||
|
||||
av_bprint_init(&pbuf, 0, 1);
|
||||
av_bprint_channel_layout(&pbuf, channels, layout);
|
||||
|
||||
char *result = av_mallocz(pbuf.len);
|
||||
|
||||
memcpy(result, pbuf.str, pbuf.len);
|
||||
|
||||
av_bprint_clear(&pbuf);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
AVCOL_RANGE_UNSPECIFIED = iota
|
||||
AVCOL_RANGE_MPEG ///< the normal 219*2^(n-8) "MPEG" YUV ranges
|
||||
AVCOL_RANGE_JPEG ///< the normal 2^n-1 "JPEG" YUV ranges
|
||||
AVCOL_RANGE_NB ///< Not part of ABI
|
||||
)
|
||||
|
||||
var (
|
||||
AV_CODEC_ID_MPEG1VIDEO int = C.AV_CODEC_ID_MPEG1VIDEO
|
||||
AV_CODEC_ID_MPEG2VIDEO int = C.AV_CODEC_ID_MPEG2VIDEO
|
||||
AV_CODEC_ID_H264 int = C.AV_CODEC_ID_H264
|
||||
AV_CODEC_ID_MPEG4 int = C.AV_CODEC_ID_MPEG4
|
||||
AV_CODEC_ID_JPEG2000 int = C.AV_CODEC_ID_JPEG2000
|
||||
AV_CODEC_ID_MJPEG int = C.AV_CODEC_ID_MJPEG
|
||||
AV_CODEC_ID_MSMPEG4V1 int = C.AV_CODEC_ID_MSMPEG4V1
|
||||
AV_CODEC_ID_MSMPEG4V2 int = C.AV_CODEC_ID_MSMPEG4V2
|
||||
AV_CODEC_ID_MSMPEG4V3 int = C.AV_CODEC_ID_MSMPEG4V3
|
||||
AV_CODEC_ID_WMV1 int = C.AV_CODEC_ID_WMV1
|
||||
AV_CODEC_ID_WMV2 int = C.AV_CODEC_ID_WMV2
|
||||
AV_CODEC_ID_FLV1 int = C.AV_CODEC_ID_FLV1
|
||||
AV_CODEC_ID_PNG int = C.AV_CODEC_ID_PNG
|
||||
AV_CODEC_ID_TIFF int = C.AV_CODEC_ID_TIFF
|
||||
AV_CODEC_ID_GIF int = C.AV_CODEC_ID_GIF
|
||||
AV_CODEC_ID_RAWVIDEO int = C.AV_CODEC_ID_RAWVIDEO
|
||||
|
||||
CODEC_FLAG_GLOBAL_HEADER int = C.AV_CODEC_FLAG_GLOBAL_HEADER
|
||||
FF_MB_DECISION_SIMPLE int = C.FF_MB_DECISION_SIMPLE
|
||||
FF_MB_DECISION_BITS int = C.FF_MB_DECISION_BITS
|
||||
FF_MB_DECISION_RD int = C.FF_MB_DECISION_RD
|
||||
|
||||
AV_SAMPLE_FMT_U8 int32 = C.AV_SAMPLE_FMT_U8
|
||||
AV_SAMPLE_FMT_S16 int32 = C.AV_SAMPLE_FMT_S16
|
||||
AV_SAMPLE_FMT_S32 int32 = C.AV_SAMPLE_FMT_S32
|
||||
AV_SAMPLE_FMT_FLT int32 = C.AV_SAMPLE_FMT_FLT
|
||||
AV_SAMPLE_FMT_DBL int32 = C.AV_SAMPLE_FMT_DBL
|
||||
|
||||
AV_SAMPLE_FMT_U8P int32 = C.AV_SAMPLE_FMT_U8P
|
||||
AV_SAMPLE_FMT_S16P int32 = C.AV_SAMPLE_FMT_S16P
|
||||
AV_SAMPLE_FMT_S32P int32 = C.AV_SAMPLE_FMT_S32P
|
||||
AV_SAMPLE_FMT_FLTP int32 = C.AV_SAMPLE_FMT_FLTP
|
||||
AV_SAMPLE_FMT_DBLP int32 = C.AV_SAMPLE_FMT_DBLP
|
||||
|
||||
color_range_names map[uint32]string = map[uint32]string{
|
||||
AVCOL_RANGE_UNSPECIFIED: "unknown",
|
||||
AVCOL_RANGE_MPEG: "tv",
|
||||
AVCOL_RANGE_JPEG: "pc",
|
||||
}
|
||||
)
|
||||
|
||||
type avBprint C.struct_AVBprint
|
||||
|
||||
type CodecCtx struct {
|
||||
codec *Codec
|
||||
avCodecCtx *C.struct_AVCodecContext
|
||||
CgoMemoryManage
|
||||
}
|
||||
|
||||
func NewCodecCtx(codec *Codec, options ...[]*Option) *CodecCtx {
|
||||
result := &CodecCtx{codec: codec}
|
||||
|
||||
codecctx := C.avcodec_alloc_context3(codec.avCodec)
|
||||
if codecctx == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
result.avCodecCtx = codecctx
|
||||
|
||||
// we're really expecting only one options-array —
|
||||
// variadic arg is used for backward compatibility
|
||||
if len(options) == 1 {
|
||||
for _, option := range options[0] {
|
||||
option.Set(result.avCodecCtx)
|
||||
}
|
||||
}
|
||||
|
||||
result.avCodecCtx.codec_id = codec.avCodec.id
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) SetOptions(options []Option) {
|
||||
for _, option := range options {
|
||||
option.Set(cc.avCodecCtx)
|
||||
}
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) CopyExtra(ist *Stream) *CodecCtx {
|
||||
codec := cc.avCodecCtx
|
||||
icodec := ist.CodecCtx().avCodecCtx
|
||||
|
||||
codec.bits_per_raw_sample = icodec.bits_per_raw_sample
|
||||
codec.chroma_sample_location = icodec.chroma_sample_location
|
||||
|
||||
codec.codec_id = icodec.codec_id
|
||||
codec.codec_type = icodec.codec_type
|
||||
|
||||
// codec.codec_tag = icodec.codec_tag
|
||||
|
||||
codec.rc_max_rate = icodec.rc_max_rate
|
||||
codec.rc_buffer_size = icodec.rc_buffer_size
|
||||
|
||||
codec.field_order = icodec.field_order
|
||||
|
||||
codec.extradata = (*C.uint8_t)(C.av_mallocz((C.size_t)((C.uint64_t)(icodec.extradata_size) + C.AV_INPUT_BUFFER_PADDING_SIZE)))
|
||||
|
||||
C.memcpy(unsafe.Pointer(codec.extradata), unsafe.Pointer(icodec.extradata), (C.size_t)(icodec.extradata_size))
|
||||
codec.extradata_size = icodec.extradata_size
|
||||
codec.bits_per_coded_sample = icodec.bits_per_coded_sample
|
||||
|
||||
codec.has_b_frames = icodec.has_b_frames
|
||||
|
||||
return cc
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) Open(dict *Dict) error {
|
||||
if cc.IsOpen() {
|
||||
return nil
|
||||
}
|
||||
|
||||
var avDict *C.struct_AVDictionary
|
||||
if dict != nil {
|
||||
avDict = dict.avDict
|
||||
}
|
||||
|
||||
if averr := C.avcodec_open2(cc.avCodecCtx, cc.codec.avCodec, &avDict); averr < 0 {
|
||||
return errors.New(fmt.Sprintf("Error opening codec '%s:%s', averror: %s", cc.codec.Name(), cc.codec.LongName(), AvError(int(averr))))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// codec context is freed by avformat_free_context()
|
||||
func (cc *CodecCtx) Free() {
|
||||
C.avcodec_free_context(&cc.avCodecCtx)
|
||||
cc.codec.Free()
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) CloseAndRelease() {
|
||||
panic("(CodecCtx)CloseAndRelease() is deprecated")
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) Close() {
|
||||
C.avcodec_close(cc.avCodecCtx)
|
||||
}
|
||||
|
||||
// @todo
|
||||
func (cc *CodecCtx) SetOpt() {
|
||||
// mock
|
||||
C.av_opt_set_int(unsafe.Pointer(cc.avCodecCtx), C.CString("refcounted_frames"), 1, 0)
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) Codec() *Codec {
|
||||
return &Codec{avCodec: cc.avCodecCtx.codec}
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) Id() int {
|
||||
return int(cc.avCodecCtx.codec_id)
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) Type() int32 {
|
||||
return int32(cc.avCodecCtx.codec_type)
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) Width() int {
|
||||
return int(cc.avCodecCtx.width)
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) Height() int {
|
||||
return int(cc.avCodecCtx.height)
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) PixFmt() int32 {
|
||||
return int32(cc.avCodecCtx.pix_fmt)
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) FrameSize() int {
|
||||
return int(cc.avCodecCtx.frame_size)
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) SampleFmt() int32 {
|
||||
return cc.avCodecCtx.sample_fmt
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) SampleRate() int {
|
||||
return int(cc.avCodecCtx.sample_rate)
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) Profile() int {
|
||||
return int(cc.avCodecCtx.profile)
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) IsOpen() bool {
|
||||
return (int(C.avcodec_is_open(cc.avCodecCtx)) > 0)
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) SetProfile(profile int) *CodecCtx {
|
||||
cc.avCodecCtx.profile = C.int(profile)
|
||||
return cc
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) TimeBase() AVRational {
|
||||
return AVRational(cc.avCodecCtx.time_base)
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) ChannelLayout() int {
|
||||
return int(cc.avCodecCtx.channel_layout)
|
||||
}
|
||||
func (cc *CodecCtx) SetChannelLayout(channelLayout int) {
|
||||
cc.avCodecCtx.channel_layout = C.uint64_t(channelLayout)
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) BitRate() int {
|
||||
return int(cc.avCodecCtx.bit_rate)
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) Channels() int {
|
||||
return int(cc.avCodecCtx.channels)
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) SetBitRate(val int) *CodecCtx {
|
||||
cc.avCodecCtx.bit_rate = C.int64_t(val)
|
||||
return cc
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) SetWidth(val int) *CodecCtx {
|
||||
cc.avCodecCtx.width = C.int(val)
|
||||
return cc
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) SetHeight(val int) *CodecCtx {
|
||||
cc.avCodecCtx.height = C.int(val)
|
||||
return cc
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) SetDimension(w, h int) *CodecCtx {
|
||||
cc.avCodecCtx.width = C.int(w)
|
||||
cc.avCodecCtx.height = C.int(h)
|
||||
return cc
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) SetTimeBase(val AVR) *CodecCtx {
|
||||
cc.avCodecCtx.time_base.num = C.int(val.Num)
|
||||
cc.avCodecCtx.time_base.den = C.int(val.Den)
|
||||
return cc
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) SetGopSize(val int) *CodecCtx {
|
||||
cc.avCodecCtx.gop_size = C.int(val)
|
||||
return cc
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) SetMaxBFrames(val int) *CodecCtx {
|
||||
cc.avCodecCtx.max_b_frames = C.int(val)
|
||||
return cc
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) SetPixFmt(val int32) *CodecCtx {
|
||||
cc.avCodecCtx.pix_fmt = val
|
||||
return cc
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) SetFlag(flag int) *CodecCtx {
|
||||
cc.avCodecCtx.flags |= C.int(flag)
|
||||
return cc
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) SetMbDecision(val int) *CodecCtx {
|
||||
cc.avCodecCtx.mb_decision = C.int(val)
|
||||
return cc
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) SetSampleFmt(val int32) *CodecCtx {
|
||||
if int(C.check_sample_fmt(cc.codec.avCodec, val)) == 0 {
|
||||
panic(fmt.Sprintf("encoder doesn't support sample format %s", GetSampleFmtName(val)))
|
||||
}
|
||||
|
||||
cc.avCodecCtx.sample_fmt = val
|
||||
return cc
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) SetSampleRate(val int) *CodecCtx {
|
||||
cc.avCodecCtx.sample_rate = C.int(val)
|
||||
return cc
|
||||
}
|
||||
|
||||
var (
|
||||
FF_COMPLIANCE_VERY_STRICT int = C.FF_COMPLIANCE_VERY_STRICT
|
||||
FF_COMPLIANCE_STRICT int = C.FF_COMPLIANCE_STRICT
|
||||
FF_COMPLIANCE_NORMAL int = C.FF_COMPLIANCE_NORMAL
|
||||
FF_COMPLIANCE_UNOFFICIAL int = C.FF_COMPLIANCE_UNOFFICIAL
|
||||
FF_COMPLIANCE_EXPERIMENTAL int = C.FF_COMPLIANCE_EXPERIMENTAL
|
||||
)
|
||||
|
||||
func (cc *CodecCtx) SetStrictCompliance(val int) *CodecCtx {
|
||||
cc.avCodecCtx.strict_std_compliance = C.int(val)
|
||||
return cc
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) SetHasBframes(val int) *CodecCtx {
|
||||
cc.avCodecCtx.has_b_frames = C.int(val)
|
||||
return cc
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) SetChannels(val int) *CodecCtx {
|
||||
cc.avCodecCtx.channels = C.int(val)
|
||||
return cc
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) SetFrameRate(r AVR) *CodecCtx {
|
||||
cc.avCodecCtx.framerate.num = C.int(r.Num)
|
||||
cc.avCodecCtx.framerate.den = C.int(r.Den)
|
||||
return cc
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) SetBitsPerRawSample(val int) *CodecCtx {
|
||||
cc.avCodecCtx.bits_per_raw_sample = C.int(val)
|
||||
return cc
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) SelectSampleRate() int {
|
||||
return int(C.select_sample_rate(cc.codec.avCodec))
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) SelectChannelLayout() int {
|
||||
return int(C.select_channel_layout(cc.codec.avCodec))
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) FlushBuffers() {
|
||||
C.avcodec_flush_buffers(cc.avCodecCtx)
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) Dump() {
|
||||
fmt.Println(cc.avCodecCtx)
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) GetFrameRate() AVRational {
|
||||
return AVRational(cc.avCodecCtx.framerate)
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) GetProfile() int {
|
||||
return int(cc.avCodecCtx.profile)
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) GetProfileName() string {
|
||||
return C.GoString(C.avcodec_profile_name(cc.avCodecCtx.codec_id, cc.avCodecCtx.profile))
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) GetMediaType() string {
|
||||
return C.GoString(C.av_get_media_type_string(cc.avCodecCtx.codec_type))
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) GetCodecTag() uint32 {
|
||||
return uint32(cc.avCodecCtx.codec_tag)
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) GetCodecTagName() string {
|
||||
var (
|
||||
ct uint32 = uint32(cc.avCodecCtx.codec_tag)
|
||||
result string
|
||||
)
|
||||
|
||||
for i := 0; i < 4; i++ {
|
||||
c := ct & 0xff
|
||||
result += fmt.Sprintf("%c", c)
|
||||
ct >>= 8
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%v", result)
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) GetCodedWith() int {
|
||||
return int(cc.avCodecCtx.coded_width)
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) GetCodedHeight() int {
|
||||
return int(cc.avCodecCtx.coded_height)
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) GetBFrames() int {
|
||||
return int(cc.avCodecCtx.has_b_frames)
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) GetPixFmtName() string {
|
||||
// return C.GoString(C.av_get_pix_fmt_name(cc.avCodecCtx.pix_fmt))
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) GetColorRangeName() string {
|
||||
return color_range_names[cc.avCodecCtx.color_range]
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) GetRefs() int {
|
||||
return int(cc.avCodecCtx.refs)
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) GetSampleFmtName() string {
|
||||
return C.GoString(C.av_get_sample_fmt_name(cc.avCodecCtx.sample_fmt))
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) GetChannelLayoutName() string {
|
||||
str := C.GoString(C.gmf_get_channel_layout_name(C.int(cc.Channels()), C.int(cc.ChannelLayout())))
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
func (this *CodecCtx) GetDefaultChannelLayout(ac int) int {
|
||||
return int(C.av_get_default_channel_layout(C.int(ac)))
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) GetBitsPerSample() int {
|
||||
return int(C.av_get_bits_per_sample(cc.codec.avCodec.id))
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) GetVideoSize() string {
|
||||
return fmt.Sprintf("%dx%d", cc.Width(), cc.Height())
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) GetAspectRation() AVRational {
|
||||
return AVRational(cc.avCodecCtx.sample_aspect_ratio)
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) Decode(pkt *Packet) ([]*Frame, error) {
|
||||
var (
|
||||
ret int
|
||||
result []*Frame = make([]*Frame, 0)
|
||||
)
|
||||
|
||||
if pkt == nil {
|
||||
ret = int(C.avcodec_send_packet(cc.avCodecCtx, nil))
|
||||
} else {
|
||||
ret = int(C.avcodec_send_packet(cc.avCodecCtx, &pkt.avPacket))
|
||||
}
|
||||
if ret < 0 {
|
||||
return nil, AvError(ret)
|
||||
}
|
||||
|
||||
for {
|
||||
frame := NewFrame()
|
||||
|
||||
ret = int(C.avcodec_receive_frame(cc.avCodecCtx, frame.avFrame))
|
||||
if AvErrno(ret) == syscall.EAGAIN || ret == AVERROR_EOF {
|
||||
frame.Free()
|
||||
break
|
||||
} else if ret < 0 {
|
||||
frame.Free()
|
||||
return nil, AvError(ret)
|
||||
}
|
||||
|
||||
result = append(result, frame)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) Encode(frames []*Frame, drain int) ([]*Packet, error) {
|
||||
var (
|
||||
ret int
|
||||
result []*Packet = make([]*Packet, 0)
|
||||
)
|
||||
|
||||
if len(frames) == 0 && drain >= 0 {
|
||||
frames = append(frames, nil)
|
||||
}
|
||||
|
||||
for _, frame := range frames {
|
||||
if frame == nil {
|
||||
ret = int(C.avcodec_send_frame(cc.avCodecCtx, nil))
|
||||
} else {
|
||||
ret = int(C.avcodec_send_frame(cc.avCodecCtx, frame.avFrame))
|
||||
}
|
||||
if ret < 0 {
|
||||
return nil, AvError(ret)
|
||||
}
|
||||
|
||||
for {
|
||||
pkt := NewPacket()
|
||||
ret = int(C.avcodec_receive_packet(cc.avCodecCtx, &pkt.avPacket))
|
||||
if ret < 0 {
|
||||
pkt.Free()
|
||||
break
|
||||
}
|
||||
|
||||
result = append(result, pkt)
|
||||
}
|
||||
if frame != nil {
|
||||
frame.Free()
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (cc *CodecCtx) Decode2(pkt *Packet) (*Frame, int) {
|
||||
var (
|
||||
ret int
|
||||
)
|
||||
|
||||
if pkt == nil {
|
||||
ret = int(C.avcodec_send_packet(cc.avCodecCtx, nil))
|
||||
} else {
|
||||
ret = int(C.avcodec_send_packet(cc.avCodecCtx, &pkt.avPacket))
|
||||
}
|
||||
if ret < 0 {
|
||||
return nil, ret
|
||||
}
|
||||
|
||||
frame := NewFrame()
|
||||
|
||||
if ret = int(C.avcodec_receive_frame(cc.avCodecCtx, frame.avFrame)); ret < 0 {
|
||||
return nil, ret
|
||||
}
|
||||
|
||||
return frame, 0
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
// +build go1.11,!go1.12
|
||||
|
||||
package gmf
|
||||
|
||||
/*
|
||||
213
filter_go112.go
Normal file
213
filter_go112.go
Normal file
@@ -0,0 +1,213 @@
|
||||
// +build go1.12
|
||||
|
||||
package gmf
|
||||
|
||||
/*
|
||||
|
||||
#cgo pkg-config: libavfilter
|
||||
|
||||
#include <stdio.h>
|
||||
#include <libavfilter/buffersink.h>
|
||||
#include <libavfilter/buffersrc.h>
|
||||
#include <libavutil/pixdesc.h>
|
||||
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
AV_BUFFERSINK_FLAG_PEEK = 1
|
||||
AV_BUFFERSINK_FLAG_NO_REQUEST = 2
|
||||
AV_BUFFERSRC_FLAG_PUSH = 4
|
||||
)
|
||||
|
||||
type Filter struct {
|
||||
bufferCtx []*C.AVFilterContext
|
||||
sinkCtx *C.AVFilterContext
|
||||
filterGraph *C.AVFilterGraph
|
||||
}
|
||||
|
||||
func NewFilter(desc string, srcStreams []*Stream, ost *Stream, options []*Option) (*Filter, error) {
|
||||
f := &Filter{
|
||||
filterGraph: C.avfilter_graph_alloc(),
|
||||
bufferCtx: make([]*C.AVFilterContext, 0),
|
||||
}
|
||||
|
||||
var (
|
||||
ret, i int
|
||||
args string
|
||||
inputs *C.AVFilterInOut
|
||||
outputs *C.AVFilterInOut
|
||||
curr *C.AVFilterInOut
|
||||
last *C.AVFilterContext
|
||||
)
|
||||
|
||||
cdesc := C.CString(desc)
|
||||
defer C.free(unsafe.Pointer(cdesc))
|
||||
|
||||
if ret = int(C.avfilter_graph_parse2(
|
||||
f.filterGraph,
|
||||
cdesc,
|
||||
&inputs,
|
||||
&outputs,
|
||||
)); ret < 0 {
|
||||
return f, fmt.Errorf("error parsing filter graph - %s", AvError(ret))
|
||||
}
|
||||
defer C.avfilter_inout_free(&inputs)
|
||||
defer C.avfilter_inout_free(&outputs)
|
||||
|
||||
for curr = inputs; curr != nil; curr = curr.next {
|
||||
if len(srcStreams) < i {
|
||||
return nil, fmt.Errorf("not enough of source streams")
|
||||
}
|
||||
|
||||
src := srcStreams[i]
|
||||
|
||||
args = fmt.Sprintf("video_size=%s:pix_fmt=%d:time_base=%s:pixel_aspect=%s:sws_param=flags=%d:frame_rate=%s", src.CodecCtx().GetVideoSize(), src.CodecCtx().PixFmt(), src.TimeBase().AVR(), src.CodecCtx().GetAspectRation().AVR(), SWS_BILINEAR, src.GetRFrameRate().AVR().String())
|
||||
|
||||
if last, ret = f.create("buffer", fmt.Sprintf("in_%d", i), args); ret < 0 {
|
||||
return f, fmt.Errorf("error creating input buffer - %s", AvError(ret))
|
||||
}
|
||||
|
||||
f.bufferCtx = append(f.bufferCtx, last)
|
||||
|
||||
if ret = int(C.avfilter_link(last, 0, curr.filter_ctx, C.uint(i))); ret < 0 {
|
||||
return f, fmt.Errorf("error linking filters - %s", AvError(ret))
|
||||
}
|
||||
|
||||
i++
|
||||
}
|
||||
|
||||
if f.sinkCtx, ret = f.create("buffersink", "out", ""); ret < 0 {
|
||||
return f, fmt.Errorf("error creating filter 'buffersink' - %s", AvError(ret))
|
||||
}
|
||||
|
||||
// XXX hardcoded PIXFMT!
|
||||
if last, ret = f.create("format", "format", "yuv420p"); ret < 0 {
|
||||
return f, fmt.Errorf("error creating format filter - %s", AvError(ret))
|
||||
}
|
||||
|
||||
if ret = int(C.avfilter_link(outputs.filter_ctx, 0, last, 0)); ret < 0 {
|
||||
return f, fmt.Errorf("error linking output filters - %s", AvError(ret))
|
||||
}
|
||||
|
||||
if ret = int(C.avfilter_link(last, 0, f.sinkCtx, 0)); ret < 0 {
|
||||
return f, fmt.Errorf("error linking output filters - %s", AvError(ret))
|
||||
}
|
||||
|
||||
if ret = int(C.avfilter_graph_config(f.filterGraph, nil)); ret < 0 {
|
||||
return f, fmt.Errorf("graph config error - %s", AvError(ret))
|
||||
}
|
||||
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func (f *Filter) create(filter, name, args string) (*C.AVFilterContext, int) {
|
||||
var (
|
||||
ctx *C.AVFilterContext
|
||||
ret int
|
||||
)
|
||||
|
||||
cfilter := C.CString(filter)
|
||||
cname := C.CString(name)
|
||||
cargs := C.CString(args)
|
||||
|
||||
ret = int(C.avfilter_graph_create_filter(
|
||||
&ctx,
|
||||
C.avfilter_get_by_name(cfilter),
|
||||
cname,
|
||||
cargs,
|
||||
nil,
|
||||
f.filterGraph))
|
||||
|
||||
C.free(unsafe.Pointer(cfilter))
|
||||
C.free(unsafe.Pointer(cname))
|
||||
C.free(unsafe.Pointer(cargs))
|
||||
|
||||
return ctx, ret
|
||||
}
|
||||
|
||||
func (f *Filter) AddFrame(frame *Frame, istIdx int, flag int) error {
|
||||
var ret int
|
||||
|
||||
if istIdx >= len(f.bufferCtx) {
|
||||
return fmt.Errorf("unexpected stream index #%d", istIdx)
|
||||
}
|
||||
|
||||
if ret = int(C.av_buffersrc_add_frame_flags(
|
||||
f.bufferCtx[istIdx],
|
||||
frame.avFrame,
|
||||
C.int(flag)),
|
||||
); ret < 0 {
|
||||
return AvError(ret)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Filter) Close(istIdx int) error {
|
||||
var ret int
|
||||
|
||||
if ret = int(C.av_buffersrc_close(f.bufferCtx[istIdx], 0, AV_BUFFERSRC_FLAG_PUSH)); ret < 0 {
|
||||
return AvError(ret)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Filter) GetFrame() ([]*Frame, error) {
|
||||
var (
|
||||
ret int
|
||||
result []*Frame = make([]*Frame, 0)
|
||||
)
|
||||
|
||||
for {
|
||||
frame := NewFrame()
|
||||
|
||||
ret = int(C.av_buffersink_get_frame_flags(f.sinkCtx, frame.avFrame, AV_BUFFERSINK_FLAG_NO_REQUEST))
|
||||
if AvErrno(ret) == syscall.EAGAIN || ret == AVERROR_EOF {
|
||||
frame.Free()
|
||||
break
|
||||
} else if ret < 0 {
|
||||
frame.Free()
|
||||
return nil, AvError(ret)
|
||||
}
|
||||
|
||||
result = append(result, frame)
|
||||
}
|
||||
|
||||
f.RequestOldest()
|
||||
|
||||
return result, AvError(ret)
|
||||
}
|
||||
|
||||
func (f *Filter) RequestOldest() error {
|
||||
var ret int
|
||||
|
||||
if ret = int(C.avfilter_graph_request_oldest(f.filterGraph)); ret < 0 {
|
||||
return AvError(ret)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Filter) Dump() {
|
||||
fmt.Println(C.GoString(C.avfilter_graph_dump(f.filterGraph, nil)))
|
||||
}
|
||||
|
||||
func (f *Filter) Release() {
|
||||
if f.sinkCtx != nil {
|
||||
C.avfilter_free(f.sinkCtx)
|
||||
}
|
||||
|
||||
for i, _ := range f.bufferCtx {
|
||||
C.avfilter_free(f.bufferCtx[i])
|
||||
}
|
||||
|
||||
C.avfilter_graph_free(&f.filterGraph)
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
// +build go1.11,!go1.12
|
||||
|
||||
// Format.
|
||||
package gmf
|
||||
|
||||
603
format_go112.go
Normal file
603
format_go112.go
Normal file
@@ -0,0 +1,603 @@
|
||||
// +build go1.12
|
||||
|
||||
// Format.
|
||||
package gmf
|
||||
|
||||
/*
|
||||
|
||||
#cgo pkg-config: libavformat libavdevice libavfilter
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "libavformat/avformat.h"
|
||||
#include <libavdevice/avdevice.h>
|
||||
#include "libavutil/opt.h"
|
||||
|
||||
static AVStream* gmf_get_stream(AVFormatContext *ctx, int idx) {
|
||||
return ctx->streams[idx];
|
||||
}
|
||||
|
||||
static int gmf_alloc_priv_data(AVFormatContext *s, AVDictionary **options) {
|
||||
AVDictionary *tmp = NULL;
|
||||
|
||||
if (options)
|
||||
av_dict_copy(&tmp, *options, 0);
|
||||
|
||||
if (s->iformat->priv_data_size > 0) {
|
||||
if (!(s->priv_data = av_mallocz(s->iformat->priv_data_size))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (s->iformat->priv_class) {
|
||||
*(const AVClass**)s->priv_data = s->iformat->priv_class;
|
||||
av_opt_set_defaults(s->priv_data);
|
||||
if (av_opt_set_dict(s->priv_data, &tmp) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (s->iformat->priv_data_size);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *gmf_sprintf_sdp(AVFormatContext *ctx) {
|
||||
char *sdp = malloc(sizeof(char)*16384);
|
||||
av_sdp_create(&ctx, 1, sdp, sizeof(char)*16384);
|
||||
return sdp;
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
AV_LOG_QUIET int = C.AV_LOG_QUIET
|
||||
AV_LOG_PANIC int = C.AV_LOG_PANIC
|
||||
AV_LOG_FATAL int = C.AV_LOG_FATAL
|
||||
AV_LOG_ERROR int = C.AV_LOG_ERROR
|
||||
AV_LOG_WARNING int = C.AV_LOG_WARNING
|
||||
AV_LOG_INFO int = C.AV_LOG_INFO
|
||||
AV_LOG_VERBOSE int = C.AV_LOG_VERBOSE
|
||||
AV_LOG_DEBUG int = C.AV_LOG_DEBUG
|
||||
|
||||
FF_MOV_FLAG_FASTSTART = (1 << 7)
|
||||
|
||||
AVFMT_FLAG_GENPTS int = C.AVFMT_FLAG_GENPTS
|
||||
AVFMTCTX_NOHEADER int = C.AVFMTCTX_NOHEADER
|
||||
)
|
||||
|
||||
const (
|
||||
PLAYLIST_TYPE_NONE int = iota
|
||||
PLAYLIST_TYPE_EVENT
|
||||
PLAYLIST_TYPE_VOD
|
||||
PLAYLIST_TYPE_NB
|
||||
)
|
||||
|
||||
type FmtCtx struct {
|
||||
Filename string
|
||||
|
||||
avCtx *C.struct_AVFormatContext
|
||||
ofmt *OutputFmt
|
||||
streams map[int]*Stream
|
||||
customPb bool
|
||||
}
|
||||
|
||||
func init() {
|
||||
C.avformat_network_init()
|
||||
C.avdevice_register_all()
|
||||
}
|
||||
|
||||
func LogSetLevel(level int) {
|
||||
C.av_log_set_level(C.int(level))
|
||||
}
|
||||
|
||||
// @todo return error if avCtx is null
|
||||
// @todo start_time is it needed?
|
||||
func NewCtx(options ...[]Option) *FmtCtx {
|
||||
ctx := &FmtCtx{
|
||||
avCtx: C.avformat_alloc_context(),
|
||||
streams: make(map[int]*Stream),
|
||||
customPb: false,
|
||||
}
|
||||
|
||||
ctx.avCtx.start_time = 0
|
||||
|
||||
if len(options) == 1 {
|
||||
for _, option := range options[0] {
|
||||
option.Set(ctx.avCtx)
|
||||
}
|
||||
}
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
||||
func NewOutputCtx(i interface{}, options ...[]Option) (*FmtCtx, error) {
|
||||
this := &FmtCtx{streams: make(map[int]*Stream)}
|
||||
|
||||
switch t := i.(type) {
|
||||
case string:
|
||||
this.ofmt = FindOutputFmt("", i.(string), "")
|
||||
|
||||
case *OutputFmt:
|
||||
this.ofmt = i.(*OutputFmt)
|
||||
|
||||
default:
|
||||
return nil, errors.New(fmt.Sprintf("unexpected type %v", t))
|
||||
}
|
||||
|
||||
if this.ofmt == nil {
|
||||
return nil, errors.New(fmt.Sprintf("output format is not initialized. Unable to allocate context"))
|
||||
}
|
||||
|
||||
cfilename := C.CString(this.ofmt.Filename)
|
||||
defer C.free(unsafe.Pointer(cfilename))
|
||||
|
||||
C.avformat_alloc_output_context2(&this.avCtx, this.ofmt.avOutputFmt, nil, cfilename)
|
||||
if this.avCtx == nil {
|
||||
return nil, errors.New(fmt.Sprintf("unable to allocate context"))
|
||||
}
|
||||
|
||||
C.av_opt_set_defaults(unsafe.Pointer(this.avCtx))
|
||||
|
||||
if len(options) == 1 {
|
||||
for _, option := range options[0] {
|
||||
option.Set(this.avCtx)
|
||||
}
|
||||
}
|
||||
|
||||
this.Filename = this.ofmt.Filename
|
||||
|
||||
return this, nil
|
||||
}
|
||||
|
||||
func NewOutputCtxWithFormatName(filename, format string) (*FmtCtx, error) {
|
||||
this := &FmtCtx{streams: make(map[int]*Stream)}
|
||||
|
||||
cfilename := C.CString(filename)
|
||||
defer C.free(unsafe.Pointer(cfilename))
|
||||
|
||||
cFormat := C.CString(format)
|
||||
defer C.free(unsafe.Pointer(cFormat))
|
||||
|
||||
C.avformat_alloc_output_context2(&this.avCtx, nil, cFormat, cfilename)
|
||||
|
||||
if this.avCtx == nil {
|
||||
return nil, errors.New(fmt.Sprintf("unable to allocate context"))
|
||||
}
|
||||
|
||||
this.Filename = filename
|
||||
|
||||
this.ofmt = &OutputFmt{Filename: filename, avOutputFmt: this.avCtx.oformat}
|
||||
|
||||
return this, nil
|
||||
}
|
||||
|
||||
// Just a helper for NewCtx().OpenInput()
|
||||
func NewInputCtx(filename string) (*FmtCtx, error) {
|
||||
ctx := NewCtx()
|
||||
|
||||
if ctx.avCtx == nil {
|
||||
return nil, errors.New(fmt.Sprintf("unable to allocate context"))
|
||||
}
|
||||
|
||||
if err := ctx.OpenInput(filename); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
func NewInputCtxWithFormatName(filename, format string) (*FmtCtx, error) {
|
||||
ctx := NewCtx()
|
||||
|
||||
if ctx.avCtx == nil {
|
||||
return nil, errors.New(fmt.Sprintf("unable to allocate context"))
|
||||
}
|
||||
if err := ctx.SetInputFormat(format); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := ctx.OpenInput(filename); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
func (this *FmtCtx) SetOptions(options []*Option) {
|
||||
for _, option := range options {
|
||||
option.Set(this.avCtx)
|
||||
}
|
||||
}
|
||||
|
||||
func (this *FmtCtx) OpenInput(filename string) error {
|
||||
var (
|
||||
cfilename *C.char
|
||||
options *C.struct_AVDictionary = nil
|
||||
)
|
||||
|
||||
if filename == "" {
|
||||
cfilename = nil
|
||||
} else {
|
||||
cfilename = C.CString(filename)
|
||||
defer C.free(unsafe.Pointer(cfilename))
|
||||
}
|
||||
|
||||
if averr := C.avformat_open_input(&this.avCtx, cfilename, nil, &options); averr < 0 {
|
||||
return errors.New(fmt.Sprintf("Error opening input '%s': %s", filename, AvError(int(averr))))
|
||||
}
|
||||
|
||||
if averr := C.avformat_find_stream_info(this.avCtx, nil); averr < 0 {
|
||||
return errors.New(fmt.Sprintf("Unable to find stream info: %s", AvError(int(averr))))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *FmtCtx) AddStreamWithCodeCtx(codeCtx *CodecCtx) (*Stream, error) {
|
||||
var ost *Stream
|
||||
|
||||
// Create Video stream in output context
|
||||
if ost = this.NewStream(codeCtx.Codec()); ost == nil {
|
||||
return nil, fmt.Errorf("unable to create stream in context, filename: %s", this.Filename)
|
||||
}
|
||||
|
||||
ost.DumpContexCodec(codeCtx)
|
||||
|
||||
if this.avCtx.oformat != nil && int(this.avCtx.oformat.flags&C.AVFMT_GLOBALHEADER) > 0 {
|
||||
ost.SetCodecFlags()
|
||||
}
|
||||
|
||||
return ost, nil
|
||||
}
|
||||
|
||||
func (this *FmtCtx) WriteTrailer() {
|
||||
C.av_write_trailer(this.avCtx)
|
||||
}
|
||||
|
||||
func (this *FmtCtx) IsNoFile() bool {
|
||||
return this.avCtx.oformat != nil && (this.avCtx.oformat.flags&C.AVFMT_NOFILE) != 0
|
||||
}
|
||||
|
||||
func (this *FmtCtx) IsGlobalHeader() bool {
|
||||
return this.avCtx != nil && this.avCtx.oformat != nil && (this.avCtx.oformat.flags&C.AVFMT_GLOBALHEADER) != 0
|
||||
}
|
||||
|
||||
func (this *FmtCtx) WriteHeader() error {
|
||||
cfilename := &(this.avCtx.filename[0])
|
||||
|
||||
// If NOFILE flag isn't set and we don't use custom IO, open it
|
||||
if !this.IsNoFile() && !this.customPb {
|
||||
if averr := C.avio_open(&this.avCtx.pb, cfilename, C.AVIO_FLAG_WRITE); averr < 0 {
|
||||
return errors.New(fmt.Sprintf("Unable to open '%s': %s", this.Filename, AvError(int(averr))))
|
||||
}
|
||||
}
|
||||
|
||||
if averr := C.avformat_write_header(this.avCtx, nil); averr < 0 {
|
||||
return errors.New(fmt.Sprintf("Unable to write header to '%s': %s", this.Filename, AvError(int(averr))))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *FmtCtx) WritePacket(p *Packet) error {
|
||||
if averr := C.av_interleaved_write_frame(this.avCtx, &p.avPacket); averr < 0 {
|
||||
return errors.New(fmt.Sprintf("Unable to write packet to '%s': %s", this.Filename, AvError(int(averr))))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *FmtCtx) WritePacketNoBuffer(p *Packet) error {
|
||||
if averr := C.av_write_frame(this.avCtx, &p.avPacket); averr < 0 {
|
||||
return errors.New(fmt.Sprintf("Unable to write packet to '%s': %s", this.Filename, AvError(int(averr))))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *FmtCtx) SetOformat(ofmt *OutputFmt) error {
|
||||
if ofmt == nil {
|
||||
return errors.New("'ofmt' is not initialized.")
|
||||
}
|
||||
|
||||
if averr := C.avformat_alloc_output_context2(&this.avCtx, ofmt.avOutputFmt, nil, nil); averr < 0 {
|
||||
return errors.New(fmt.Sprintf("Error creating output context: %s", AvError(int(averr))))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *FmtCtx) Dump() {
|
||||
if this.ofmt == nil {
|
||||
C.av_dump_format(this.avCtx, 0, &(this.avCtx.filename[0]), 0)
|
||||
} else {
|
||||
C.av_dump_format(this.avCtx, 0, &(this.avCtx.filename[0]), 1)
|
||||
}
|
||||
}
|
||||
|
||||
func (this *FmtCtx) DumpAv() {
|
||||
fmt.Println("AVCTX:\n", this.avCtx, "\niformat:\n", this.avCtx.iformat)
|
||||
fmt.Println("flags:", this.avCtx.flags)
|
||||
}
|
||||
|
||||
func (this *FmtCtx) GetNextPacket() (*Packet, error) {
|
||||
pkt := NewPacket()
|
||||
|
||||
for {
|
||||
ret := int(C.av_read_frame(this.avCtx, &pkt.avPacket))
|
||||
|
||||
if AvErrno(ret) == syscall.EAGAIN {
|
||||
time.Sleep(10000 * time.Microsecond)
|
||||
continue
|
||||
}
|
||||
if ret == AVERROR_EOF {
|
||||
return nil, io.EOF
|
||||
}
|
||||
if ret < 0 {
|
||||
return nil, AvError(ret)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
return pkt, nil
|
||||
}
|
||||
|
||||
func (this *FmtCtx) NewStream(c *Codec) *Stream {
|
||||
var avCodec *C.struct_AVCodec = nil
|
||||
|
||||
if c != nil {
|
||||
avCodec = c.avCodec
|
||||
}
|
||||
|
||||
if st := C.avformat_new_stream(this.avCtx, avCodec); st == nil {
|
||||
return nil
|
||||
} else {
|
||||
this.streams[int(st.index)] = &Stream{avStream: st}
|
||||
Retain(this.streams[int(st.index)])
|
||||
return this.streams[int(st.index)]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Original structure member is called instead of len(this.streams)
|
||||
// because there is no initialized Stream wrappers in input context.
|
||||
func (this *FmtCtx) StreamsCnt() int {
|
||||
return int(this.avCtx.nb_streams)
|
||||
}
|
||||
|
||||
func (this *FmtCtx) GetStream(idx int) (*Stream, error) {
|
||||
if idx > this.StreamsCnt()-1 || this.StreamsCnt() == 0 {
|
||||
return nil, errors.New(fmt.Sprintf("Stream index '%d' is out of range. There is only '%d' streams.", idx, this.StreamsCnt()))
|
||||
}
|
||||
|
||||
if _, ok := this.streams[idx]; !ok {
|
||||
// create instance of Stream wrapper, when stream was initialized
|
||||
// by demuxer. it means that this is an input context.
|
||||
this.streams[idx] = &Stream{
|
||||
avStream: C.gmf_get_stream(this.avCtx, C.int(idx)),
|
||||
}
|
||||
}
|
||||
|
||||
return this.streams[idx], nil
|
||||
}
|
||||
|
||||
func (this *FmtCtx) GetBestStream(typ int32) (*Stream, error) {
|
||||
idx := C.av_find_best_stream(this.avCtx, typ, -1, -1, nil, 0)
|
||||
if int(idx) < 0 {
|
||||
return nil, errors.New(fmt.Sprintf("stream type %d not found", typ))
|
||||
}
|
||||
|
||||
return this.GetStream(int(idx))
|
||||
}
|
||||
|
||||
func (this *FmtCtx) FindStreamInfo() error {
|
||||
if averr := C.avformat_find_stream_info(this.avCtx, nil); averr < 0 {
|
||||
return errors.New(fmt.Sprintf("unable to find stream info: %s", AvError(int(averr))))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *FmtCtx) SetInputFormat(name string) error {
|
||||
cname := C.CString(name)
|
||||
defer C.free(unsafe.Pointer(cname))
|
||||
|
||||
if this.avCtx.iformat = (*C.struct_AVInputFormat)(C.av_find_input_format(cname)); this.avCtx.iformat == nil {
|
||||
return errors.New("unable to find format for name: " + name)
|
||||
}
|
||||
|
||||
if int(C.gmf_alloc_priv_data(this.avCtx, nil)) < 0 {
|
||||
return errors.New("unable to allocate priv_data")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *FmtCtx) Close() {
|
||||
if this.ofmt == nil {
|
||||
this.CloseInput()
|
||||
} else {
|
||||
this.CloseOutput()
|
||||
}
|
||||
}
|
||||
|
||||
func (this *FmtCtx) CloseInput() {
|
||||
if this.avCtx != nil {
|
||||
C.avformat_close_input(&this.avCtx)
|
||||
}
|
||||
}
|
||||
|
||||
func (this *FmtCtx) CloseOutput() {
|
||||
if this.avCtx == nil {
|
||||
return
|
||||
}
|
||||
if this.IsNoFile() {
|
||||
return
|
||||
}
|
||||
|
||||
if this.avCtx.pb != nil && !this.customPb {
|
||||
C.avio_close(this.avCtx.pb)
|
||||
}
|
||||
}
|
||||
|
||||
func (this *FmtCtx) Free() {
|
||||
this.Close()
|
||||
|
||||
if this.avCtx != nil {
|
||||
C.avformat_free_context(this.avCtx)
|
||||
}
|
||||
}
|
||||
|
||||
func (this *FmtCtx) Duration() int {
|
||||
us := int(this.avCtx.duration) % AV_TIME_BASE
|
||||
fmt.Printf("us: %v\n", us)
|
||||
fmt.Printf("duration: %v\n", int(this.avCtx.duration))
|
||||
return int(this.avCtx.duration) / AV_TIME_BASE
|
||||
}
|
||||
|
||||
// Total stream bitrate in bit/s
|
||||
func (this *FmtCtx) BitRate() int64 {
|
||||
return int64(this.avCtx.bit_rate)
|
||||
}
|
||||
|
||||
func (this *FmtCtx) StartTime() int {
|
||||
return int(this.avCtx.start_time)
|
||||
}
|
||||
|
||||
func (this *FmtCtx) SetStartTime(val int) *FmtCtx {
|
||||
this.avCtx.start_time = C.int64_t(val)
|
||||
return this
|
||||
}
|
||||
|
||||
func (this *FmtCtx) TsOffset(stime int) int {
|
||||
// temp solution. see ffmpeg_opt.c:899
|
||||
return (0 - stime)
|
||||
}
|
||||
|
||||
func (this *FmtCtx) SetDebug(val int) *FmtCtx {
|
||||
this.avCtx.debug = C.int(val)
|
||||
return this
|
||||
}
|
||||
|
||||
func (this *FmtCtx) SetFlag(flag int) *FmtCtx {
|
||||
this.avCtx.flags |= C.int(flag)
|
||||
return this
|
||||
}
|
||||
|
||||
func (this *FmtCtx) SeekFile(ist *Stream, minTs, maxTs int64, flag int) error {
|
||||
if ret := int(C.avformat_seek_file(this.avCtx, C.int(ist.Index()), C.int64_t(0), C.int64_t(minTs), C.int64_t(maxTs), C.int(flag))); ret < 0 {
|
||||
return errors.New(fmt.Sprintf("Error creating output context: %s", AvError(ret)))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *FmtCtx) SeekFrameAt(sec int64, streamIndex int) error {
|
||||
ist, err := this.GetStream(streamIndex)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
frameTs := Rescale(sec*1000, int64(ist.TimeBase().AVR().Den), int64(ist.TimeBase().AVR().Num)) / 1000
|
||||
|
||||
if err := this.SeekFile(ist, frameTs, frameTs, C.AVSEEK_FLAG_FRAME); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ist.CodecCtx().FlushBuffers()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *FmtCtx) SetPb(val *AVIOContext) *FmtCtx {
|
||||
this.avCtx.pb = val.avAVIOContext
|
||||
this.customPb = true
|
||||
return this
|
||||
}
|
||||
|
||||
func (this *FmtCtx) GetSDPString() (sdp string) {
|
||||
sdpChar := C.gmf_sprintf_sdp(this.avCtx)
|
||||
defer C.free(unsafe.Pointer(sdpChar))
|
||||
|
||||
return C.GoString(sdpChar)
|
||||
}
|
||||
|
||||
func (this *FmtCtx) WriteSDPFile(filename string) error {
|
||||
file, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return errors.New(fmt.Sprintf("Error open file:%s,error message:%s", filename, err))
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
file.WriteString(this.GetSDPString())
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *FmtCtx) Position() int {
|
||||
return int(this.avCtx.pb.pos)
|
||||
}
|
||||
|
||||
func (this *FmtCtx) SetProbeSize(v int64) {
|
||||
this.avCtx.probesize = C.int64_t(v)
|
||||
}
|
||||
|
||||
func (this *FmtCtx) GetProbeSize() int64 {
|
||||
return int64(this.avCtx.probesize)
|
||||
}
|
||||
|
||||
type OutputFmt struct {
|
||||
Filename string
|
||||
avOutputFmt *C.struct_AVOutputFormat
|
||||
CgoMemoryManage
|
||||
}
|
||||
|
||||
func FindOutputFmt(format string, filename string, mime string) *OutputFmt {
|
||||
cformat := C.CString(format)
|
||||
defer C.free(unsafe.Pointer(cformat))
|
||||
|
||||
cfilename := C.CString(filename)
|
||||
defer C.free(unsafe.Pointer(cfilename))
|
||||
|
||||
cmime := C.CString(mime)
|
||||
defer C.free(unsafe.Pointer(cmime))
|
||||
|
||||
var ofmt *C.struct_AVOutputFormat
|
||||
|
||||
if ofmt = C.av_guess_format(cformat, nil, cmime); ofmt == nil {
|
||||
ofmt = C.av_guess_format(nil, cfilename, cmime)
|
||||
}
|
||||
|
||||
if ofmt == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &OutputFmt{Filename: filename, avOutputFmt: ofmt}
|
||||
}
|
||||
|
||||
func (this *OutputFmt) Free() {
|
||||
fmt.Printf("(this *OutputFmt)Free()\n")
|
||||
}
|
||||
|
||||
func (this *OutputFmt) Name() string {
|
||||
return C.GoString(this.avOutputFmt.name)
|
||||
}
|
||||
|
||||
func (this *OutputFmt) LongName() string {
|
||||
return C.GoString(this.avOutputFmt.long_name)
|
||||
}
|
||||
|
||||
func (this *OutputFmt) MimeType() string {
|
||||
return C.GoString(this.avOutputFmt.mime_type)
|
||||
}
|
||||
|
||||
func (this *OutputFmt) Infomation() string {
|
||||
return this.Filename + ":" + this.Name() + "#" + this.LongName() + "#" + this.MimeType()
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
// +build go1.11,!go1.12
|
||||
|
||||
package gmf
|
||||
|
||||
/*
|
||||
259
frame_go112.go
Normal file
259
frame_go112.go
Normal file
@@ -0,0 +1,259 @@
|
||||
// +build go1.12
|
||||
|
||||
package gmf
|
||||
|
||||
/*
|
||||
#cgo pkg-config: libavcodec libavutil
|
||||
|
||||
#include "libavcodec/avcodec.h"
|
||||
#include "libavutil/frame.h"
|
||||
#include "libavutil/imgutils.h"
|
||||
#include "libavutil/timestamp.h"
|
||||
|
||||
void gmf_set_frame_data(AVFrame *frame, int idx, int l_size, uint8_t data) {
|
||||
if(!frame) {
|
||||
fprintf(stderr, "frame is NULL\n");
|
||||
}
|
||||
|
||||
frame->data[idx][l_size] = data;
|
||||
}
|
||||
|
||||
int gmf_get_frame_line_size(AVFrame *frame, int idx) {
|
||||
return frame->linesize[idx];
|
||||
}
|
||||
|
||||
void gmf_free_data(AVFrame *frame) {
|
||||
av_freep(&frame->data[0]);
|
||||
}
|
||||
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type Frame struct {
|
||||
avFrame *C.struct_AVFrame
|
||||
samples *C.uint8_t
|
||||
mediaType int32
|
||||
err error
|
||||
freeData bool
|
||||
}
|
||||
|
||||
func NewFrame() *Frame {
|
||||
return &Frame{avFrame: C.av_frame_alloc()}
|
||||
}
|
||||
|
||||
func (f *Frame) Encode(enc *CodecCtx) (*Packet, error) {
|
||||
pkt := NewPacket()
|
||||
if pkt == nil {
|
||||
return nil, fmt.Errorf("unable to initialize new packet")
|
||||
}
|
||||
|
||||
if ret := int(C.avcodec_send_frame(enc.avCodecCtx, f.avFrame)); ret < 0 {
|
||||
return nil, fmt.Errorf("error sending frame - %v", AvErrno(ret))
|
||||
}
|
||||
|
||||
for {
|
||||
ret := int(C.avcodec_receive_packet(enc.avCodecCtx, &pkt.avPacket))
|
||||
if AvErrno(ret) == syscall.EAGAIN {
|
||||
return nil, nil
|
||||
}
|
||||
if ret < 0 {
|
||||
return nil, fmt.Errorf("%v", AvErrno(ret))
|
||||
}
|
||||
if ret >= 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return pkt, nil
|
||||
}
|
||||
|
||||
func (f *Frame) Pts() int64 {
|
||||
return int64(f.avFrame.pts)
|
||||
}
|
||||
|
||||
func (f *Frame) Unref() {
|
||||
C.av_frame_unref(f.avFrame)
|
||||
}
|
||||
|
||||
func (f *Frame) SetPts(val int64) {
|
||||
f.avFrame.pts = (C.int64_t)(val)
|
||||
}
|
||||
|
||||
// AVPixelFormat for video frames, AVSampleFormat for audio
|
||||
func (f *Frame) Format() int {
|
||||
return int(f.avFrame.format)
|
||||
}
|
||||
|
||||
func (f *Frame) Width() int {
|
||||
return int(f.avFrame.width)
|
||||
}
|
||||
|
||||
func (f *Frame) Height() int {
|
||||
return int(f.avFrame.height)
|
||||
}
|
||||
|
||||
func (f *Frame) PktPts() int64 {
|
||||
return int64(f.avFrame.pkt_pts)
|
||||
}
|
||||
|
||||
func (f *Frame) SetPktPts(val int64) {
|
||||
f.avFrame.pkt_pts = (C.int64_t)(val)
|
||||
}
|
||||
|
||||
func (f *Frame) PktDts() int {
|
||||
return int(f.avFrame.pkt_dts)
|
||||
}
|
||||
|
||||
func (f *Frame) SetPktDts(val int) {
|
||||
f.avFrame.pkt_dts = (C.int64_t)(val)
|
||||
}
|
||||
|
||||
func (f *Frame) KeyFrame() int {
|
||||
return int(f.avFrame.key_frame)
|
||||
}
|
||||
|
||||
func (f *Frame) NbSamples() int {
|
||||
return int(f.avFrame.nb_samples)
|
||||
}
|
||||
|
||||
func (f *Frame) Channels() int {
|
||||
return int(f.avFrame.channels)
|
||||
}
|
||||
|
||||
func (f *Frame) SetFormat(val int32) *Frame {
|
||||
f.avFrame.format = C.int(val)
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *Frame) SetWidth(val int) *Frame {
|
||||
f.avFrame.width = C.int(val)
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *Frame) SetHeight(val int) *Frame {
|
||||
f.avFrame.height = C.int(val)
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *Frame) ImgAlloc() error {
|
||||
if ret := int(C.av_image_alloc(
|
||||
(**C.uint8_t)(unsafe.Pointer(&f.avFrame.data)),
|
||||
(*C.int)(unsafe.Pointer(&f.avFrame.linesize)),
|
||||
C.int(f.Width()), C.int(f.Height()), int32(f.Format()), 32)); ret < 0 {
|
||||
return errors.New(fmt.Sprintf("Unable to allocate raw image buffer: %v", AvError(ret)))
|
||||
}
|
||||
|
||||
f.freeData = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewAudioFrame(sampleFormat int32, channels, nb_samples int) (*Frame, error) {
|
||||
f := NewFrame()
|
||||
f.mediaType = AVMEDIA_TYPE_AUDIO
|
||||
f.SetNbSamples(nb_samples)
|
||||
f.SetFormat(sampleFormat)
|
||||
f.SetChannels(channels)
|
||||
|
||||
//the codec gives us the frame size, in samples,
|
||||
//we calculate the size of the samples buffer in bytes
|
||||
size := C.av_samples_get_buffer_size(nil, C.int(channels), C.int(nb_samples),
|
||||
sampleFormat, 0)
|
||||
if size < 0 {
|
||||
return nil, errors.New("could not get sample buffer size")
|
||||
}
|
||||
|
||||
f.samples = (*C.uint8_t)(C.av_malloc(C.size_t(size)))
|
||||
if f.samples == nil {
|
||||
return nil, errors.New(fmt.Sprintf("could not allocate %d bytes for samples buffer", size))
|
||||
}
|
||||
|
||||
//setup the data pointers in the AVFrame
|
||||
ret := int(C.avcodec_fill_audio_frame(f.avFrame, C.int(channels), sampleFormat,
|
||||
f.samples, C.int(size), 0))
|
||||
if ret < 0 {
|
||||
return nil, errors.New("could not setup audio frame")
|
||||
}
|
||||
|
||||
f.freeData = true
|
||||
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func (f *Frame) SetData(idx int, lineSize int, data int) *Frame {
|
||||
C.gmf_set_frame_data(f.avFrame, C.int(idx), C.int(lineSize), (C.uint8_t)(data))
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *Frame) LineSize(idx int) int {
|
||||
return int(C.gmf_get_frame_line_size(f.avFrame, C.int(idx)))
|
||||
}
|
||||
|
||||
func (f *Frame) Dump() {
|
||||
fmt.Printf("%v\n", f.avFrame)
|
||||
}
|
||||
|
||||
func (f *Frame) CloneNewFrame() *Frame {
|
||||
return &Frame{avFrame: C.av_frame_clone(f.avFrame)}
|
||||
}
|
||||
|
||||
func (f *Frame) Free() {
|
||||
if f.freeData && f.avFrame != nil {
|
||||
C.gmf_free_data(f.avFrame)
|
||||
}
|
||||
if f.avFrame != nil {
|
||||
C.av_frame_free(&f.avFrame)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Frame) SetNbSamples(val int) *Frame {
|
||||
f.avFrame.nb_samples = C.int(val)
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *Frame) SetChannelLayout(val int) *Frame {
|
||||
f.avFrame.channel_layout = (C.uint64_t)(val)
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *Frame) GetChannelLayout() int {
|
||||
return int(f.avFrame.channel_layout)
|
||||
}
|
||||
|
||||
func (f *Frame) SetChannels(val int) *Frame {
|
||||
f.avFrame.channels = C.int(val)
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *Frame) SetQuality(val int) *Frame {
|
||||
f.avFrame.quality = C.int(val)
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *Frame) SetPictType() {
|
||||
f.avFrame.pict_type = C.AV_PICTURE_TYPE_NONE
|
||||
}
|
||||
|
||||
func (f *Frame) IsNil() bool {
|
||||
if f.avFrame == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (f *Frame) GetRawFrame() *C.struct_AVFrame {
|
||||
return f.avFrame
|
||||
}
|
||||
|
||||
func (f *Frame) Time(timebase AVRational) int {
|
||||
return int(float64(timebase.AVR().Num) / float64(timebase.AVR().Den) * float64(f.Pts()))
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
// +build go1.11,!go1.12
|
||||
|
||||
package gmf
|
||||
|
||||
/*
|
||||
109
packet_go112.go
Normal file
109
packet_go112.go
Normal file
@@ -0,0 +1,109 @@
|
||||
// +build go1.12
|
||||
|
||||
package gmf
|
||||
|
||||
/*
|
||||
#cgo pkg-config: libavcodec
|
||||
|
||||
#include "libavcodec/avcodec.h"
|
||||
|
||||
void shift_data(AVPacket *pkt, int offset) {
|
||||
pkt->data += offset;
|
||||
pkt->size -= offset;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type Packet struct {
|
||||
avPacket C.struct_AVPacket
|
||||
}
|
||||
|
||||
func NewPacket() *Packet {
|
||||
p := &Packet{}
|
||||
|
||||
C.av_init_packet(&p.avPacket)
|
||||
|
||||
p.avPacket.data = nil
|
||||
p.avPacket.size = 0
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *Packet) Pts() int64 {
|
||||
return int64(p.avPacket.pts)
|
||||
}
|
||||
|
||||
func (p *Packet) SetPts(pts int64) {
|
||||
p.avPacket.pts = C.int64_t(pts)
|
||||
}
|
||||
|
||||
func (p *Packet) Dts() int64 {
|
||||
return int64(p.avPacket.dts)
|
||||
}
|
||||
|
||||
func (p *Packet) SetDts(val int64) {
|
||||
p.avPacket.dts = C.int64_t(val)
|
||||
}
|
||||
|
||||
func (p *Packet) Flags() int {
|
||||
return int(p.avPacket.flags)
|
||||
}
|
||||
|
||||
func (p *Packet) Duration() int64 {
|
||||
return int64(p.avPacket.duration)
|
||||
}
|
||||
|
||||
func (p *Packet) SetDuration(duration int64) {
|
||||
p.avPacket.duration = C.int64_t(duration)
|
||||
}
|
||||
|
||||
func (p *Packet) StreamIndex() int {
|
||||
return int(p.avPacket.stream_index)
|
||||
}
|
||||
|
||||
func (p *Packet) Size() int {
|
||||
return int(p.avPacket.size)
|
||||
}
|
||||
|
||||
func (p *Packet) Pos() int64 {
|
||||
return int64(p.avPacket.pos)
|
||||
}
|
||||
|
||||
func (p *Packet) Data() []byte {
|
||||
return C.GoBytes(unsafe.Pointer(p.avPacket.data), C.int(p.avPacket.size))
|
||||
}
|
||||
|
||||
func (p *Packet) Clone() *Packet {
|
||||
np := NewPacket()
|
||||
|
||||
C.av_packet_ref(&np.avPacket, &p.avPacket)
|
||||
|
||||
return np
|
||||
}
|
||||
|
||||
func (p *Packet) Dump() {
|
||||
fmt.Printf("idx: %d\npts: %d\ndts: %d\nsize: %d\nduration:%d\npos:%d\ndata: % x\n", p.StreamIndex(), p.avPacket.pts, p.avPacket.dts, p.avPacket.size, p.avPacket.duration, p.avPacket.pos, C.GoBytes(unsafe.Pointer(p.avPacket.data), 128))
|
||||
fmt.Println("------------------------------")
|
||||
|
||||
}
|
||||
|
||||
func (p *Packet) SetStreamIndex(val int) *Packet {
|
||||
p.avPacket.stream_index = C.int(val)
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *Packet) Free() {
|
||||
C.av_packet_unref(&p.avPacket)
|
||||
}
|
||||
|
||||
func (p *Packet) Time(timebase AVRational) int {
|
||||
return int(float64(timebase.AVR().Num) / float64(timebase.AVR().Den) * float64(p.Pts()))
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
// +build go1.11,!go1.12
|
||||
|
||||
package gmf
|
||||
|
||||
/*
|
||||
56
samplefmt_go112.go
Normal file
56
samplefmt_go112.go
Normal file
@@ -0,0 +1,56 @@
|
||||
// +build go1.12
|
||||
|
||||
package gmf
|
||||
|
||||
/*
|
||||
|
||||
#cgo pkg-config: libavutil
|
||||
|
||||
#include "libavutil/samplefmt.h"
|
||||
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
//
|
||||
// Unfinished.
|
||||
//
|
||||
|
||||
type SampleFormat int
|
||||
|
||||
type Sample struct {
|
||||
data **C.uint8_t
|
||||
linesize *int
|
||||
format SampleFormat
|
||||
CgoMemoryManage
|
||||
}
|
||||
|
||||
func NewSample(nbSamples, nbChannels int, format SampleFormat) error {
|
||||
panic("This stuff is unfinished.")
|
||||
this := &Sample{format: format}
|
||||
|
||||
if ret := int(C.av_samples_alloc_array_and_samples(
|
||||
&this.data,
|
||||
(*C.int)(unsafe.Pointer(&this.linesize)),
|
||||
C.int(nbChannels), C.int(nbSamples), int32(format), 0)); ret < 0 {
|
||||
return errors.New(fmt.Sprintf("Unable to allocate array and samples: %v", AvError(ret)))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *Sample) SampleRealloc(nbSamples, nbChannels int) error {
|
||||
if ret := int(C.av_samples_alloc(
|
||||
this.data,
|
||||
(*C.int)(unsafe.Pointer(&this.linesize)),
|
||||
C.int(nbChannels), C.int(nbSamples), int32(this.format), 0)); ret < 0 {
|
||||
return errors.New(fmt.Sprintf("Unable to allocate samples: %v", AvError(ret)))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
// +build go1.11,!go1.12
|
||||
|
||||
package gmf
|
||||
|
||||
/*
|
||||
108
sws_go112.go
Normal file
108
sws_go112.go
Normal file
@@ -0,0 +1,108 @@
|
||||
// +build go1.12
|
||||
|
||||
package gmf
|
||||
|
||||
/*
|
||||
|
||||
#cgo pkg-config: libswscale
|
||||
|
||||
#include "libswscale/swscale.h"
|
||||
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
SWS_FAST_BILINEAR int = C.SWS_FAST_BILINEAR
|
||||
SWS_BILINEAR int = C.SWS_BILINEAR
|
||||
SWS_BICUBIC int = C.SWS_BICUBIC
|
||||
SWS_X int = C.SWS_X
|
||||
SWS_POINT int = C.SWS_POINT
|
||||
SWS_AREA int = C.SWS_AREA
|
||||
SWS_BICUBLIN int = C.SWS_BICUBLIN
|
||||
SWS_GAUSS int = C.SWS_GAUSS
|
||||
SWS_SINC int = C.SWS_SINC
|
||||
SWS_LANCZOS int = C.SWS_LANCZOS
|
||||
SWS_SPLINE int = C.SWS_SPLINE
|
||||
)
|
||||
|
||||
type SwsCtx struct {
|
||||
swsCtx *C.struct_SwsContext
|
||||
width int
|
||||
height int
|
||||
pixfmt int32
|
||||
}
|
||||
|
||||
func NewSwsCtx(srcW, srcH int, srcPixFmt int32, dstW, dstH int, dstPixFmt int32, method int) (*SwsCtx, error) {
|
||||
ctx := C.sws_getContext(
|
||||
C.int(srcW),
|
||||
C.int(srcH),
|
||||
srcPixFmt,
|
||||
C.int(dstW),
|
||||
C.int(dstH),
|
||||
dstPixFmt,
|
||||
C.int(method), nil, nil, nil,
|
||||
)
|
||||
|
||||
if ctx == nil {
|
||||
return nil, fmt.Errorf("error creating sws context\n")
|
||||
}
|
||||
|
||||
return &SwsCtx{
|
||||
swsCtx: ctx,
|
||||
width: dstW,
|
||||
height: dstH,
|
||||
pixfmt: dstPixFmt,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ctx *SwsCtx) Scale(src *Frame, dst *Frame) {
|
||||
C.sws_scale(
|
||||
ctx.swsCtx,
|
||||
(**C.uint8_t)(unsafe.Pointer(&src.avFrame.data)),
|
||||
(*C.int)(unsafe.Pointer(&src.avFrame.linesize)),
|
||||
0,
|
||||
C.int(src.Height()),
|
||||
(**C.uint8_t)(unsafe.Pointer(&dst.avFrame.data)),
|
||||
(*C.int)(unsafe.Pointer(&dst.avFrame.linesize)))
|
||||
}
|
||||
|
||||
func (ctx *SwsCtx) Free() {
|
||||
if ctx.swsCtx != nil {
|
||||
C.sws_freeContext(ctx.swsCtx)
|
||||
}
|
||||
}
|
||||
|
||||
func DefaultRescaler(ctx *SwsCtx, frames []*Frame) ([]*Frame, error) {
|
||||
var (
|
||||
result []*Frame = make([]*Frame, 0)
|
||||
tmp *Frame
|
||||
err error
|
||||
)
|
||||
|
||||
for i, _ := range frames {
|
||||
tmp = NewFrame().SetWidth(ctx.width).SetHeight(ctx.height).SetFormat(ctx.pixfmt)
|
||||
if err = tmp.ImgAlloc(); err != nil {
|
||||
return nil, fmt.Errorf("error allocation tmp frame - %s", err)
|
||||
}
|
||||
|
||||
ctx.Scale(frames[i], tmp)
|
||||
|
||||
tmp.SetPts(frames[i].Pts())
|
||||
tmp.SetPktDts(frames[i].PktDts())
|
||||
|
||||
result = append(result, tmp)
|
||||
}
|
||||
|
||||
for i := 0; i < len(frames); i++ {
|
||||
if frames[i] != nil {
|
||||
frames[i].Free()
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
Reference in New Issue
Block a user