├── .gitignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── avcodec ├── avcodec.go ├── avcodec30.go ├── avcodec30_test.go ├── avcodec33.go └── avcodec_test.go ├── avfilter ├── avfilter.go ├── avfilter30.go └── avfilter_test.go ├── avformat ├── avformat.go ├── avformat30.go └── avformat_test.go ├── avutil ├── avutil.go ├── avutil_test.go ├── genhack.go ├── genhack_test.go └── hackgenerator.go └── examples └── transcoder.go /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /fixtures 3 | coverage.txt 4 | coverage.html 5 | 6 | # Intellij 7 | /*.iml 8 | /.idea 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.5 5 | - 1.6 6 | - 1.7 7 | - 1.8 8 | - tip 9 | 10 | env: 11 | - FFMPEG_VER=ffmpeg30 12 | - FFMPEG_VER=ffmpeg33 13 | 14 | before_install: 15 | - echo $TRAVIS_GO_VERSION 16 | - go get -u golang.org/x/tools/cmd/cover 17 | - "[[ $TRAVIS_GO_VERSION != 1.8* ]] || go get -u golang.org/x/lint/golint" 18 | - go get -u github.com/shirou/gopsutil 19 | - go get -u golang.org/x/sys/unix 20 | - sudo apt-get update 21 | - sudo apt-get install -y build-essential yasm libfaac-dev libmp3lame-dev libtheora-dev libvorbis-dev libvpx-dev libx264-dev libxvidcore-dev 22 | 23 | script: 24 | - "go get -d -t ./... || (cd $GOPATH/src/github.com/shirou/gopsutil && git checkout v2.17.04)" 25 | - make fixtures 26 | - make $FFMPEG_VER 27 | - go test -v ./... -tags $FFMPEG_VER 28 | - make gofmt 29 | - "[[ $TRAVIS_GO_VERSION != 1.8* ]] || make golint" 30 | - make govet 31 | - FFMPEG_TAG=$FFMPEG_VER make cover-test 32 | - FFMPEG_TAG=$FFMPEG_VER make cover 33 | 34 | after_success: 35 | - bash <(curl -s https://codecov.io/bash) 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Mario Freitas (imkira@gmail.com) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PACKAGES=$(shell find * -name *.go -print0 | xargs -0 -n1 dirname | sort --unique) 2 | TEST_PACKAGES=$(shell find * -name *_test.go -print0 | xargs -0 -n1 dirname | sort --unique) 3 | 4 | FIXTURES=sample_iPod.m4v \ 5 | sample_iTunes.mov \ 6 | sample_mpeg4.mp4 7 | 8 | .PHONY: all gofmt golint govet test clean 9 | 10 | all: gofmt golint govet test cover 11 | 12 | gofmt: 13 | @for dir in $(PACKAGES); do gofmt -s=true -d=true -l=true $${dir}; done 14 | 15 | golint: 16 | @for dir in $(PACKAGES); do golint $${dir}; done 17 | 18 | govet: 19 | @for dir in $(PACKAGES); do go tool vet -all $${dir}; done 20 | 21 | FIXTURE_TARGETS=$(addprefix fixtures/,$(FIXTURES)) 22 | 23 | $(FIXTURE_TARGETS): 24 | mkdir -p "$(dir $@)" 25 | rm -f "$@.zip" "$@" 26 | cd "$(dir $@)" && curl -L "https://bintray.com/imkira/go-libav/download_file?file_path=$(notdir $@)" -o "$(notdir $@)" 27 | rm -f "$@.zip" 28 | 29 | fixtures: $(FIXTURE_TARGETS) 30 | 31 | cover-test: 32 | rm -f coverage.* 33 | @for dir in $(TEST_PACKAGES); do (cd $${dir} && go test -v -tags $(FFMPEG_TAG) -race -cpu=1,2,4 -coverprofile=coverage.txt -covermode=atomic || touch $(PWD)/coverage.failed); done 34 | @for dir in $(TEST_PACKAGES); do (if [ -f coverage.txt ]; then cat $${dir}/coverage.txt | tail -n +2 >> coverage.txt; else cp $${dir}/coverage.txt .; fi); done 35 | @test ! -f coverage.failed || (echo Tests failed; exit 1) 36 | 37 | cover: 38 | go tool cover -html=coverage.txt -o coverage.html 39 | 40 | clean: 41 | rm -rf fixtures 42 | 43 | ffmpeg30: 44 | wget -O ffmpeg30.tar.bz2 http://ffmpeg.org/releases/ffmpeg-3.0.8.tar.bz2 45 | mkdir ffmpeg30 && tar xf ffmpeg30.tar.bz2 -C ffmpeg30 --strip-components=1 && cd ffmpeg30 && ./configure --prefix=/usr/local --disable-debug --enable-pthreads --enable-nonfree --enable-gpl --disable-indev=jack --enable-libx264 --enable-libfaac --enable-libmp3lame --enable-libtheora --enable-libvorbis --enable-libvpx --enable-libxvid --enable-libmp3lame --enable-openssl && make && sudo make install 46 | 47 | ffmpeg33: 48 | wget -O ffmpeg33.tar.bz2 http://ffmpeg.org/releases/ffmpeg-3.3.2.tar.bz2 49 | mkdir ffmpeg33 && tar xf ffmpeg33.tar.bz2 -C ffmpeg33 --strip-components=1 && cd ffmpeg33 && ./configure --prefix=/usr/local --disable-debug --enable-pthreads --enable-nonfree --enable-gpl --disable-indev=jack --enable-libx264 --enable-libmp3lame --enable-libtheora --enable-libvorbis --enable-libvpx --enable-libxvid --enable-libmp3lame --enable-openssl && make && sudo make install 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-libav 2 | 3 | [![License](http://img.shields.io/badge/license-MIT-red.svg?style=flat)](https://github.com/imkira/go-libav/blob/master/LICENSE.txt) 4 | [![GoDoc](https://godoc.org/github.com/imkira/go-libav?status.svg)](https://godoc.org/github.com/imkira/go-libav) 5 | [![Build 6 | Status](http://img.shields.io/travis/imkira/go-libav.svg?style=flat)](https://travis-ci.org/imkira/go-libav) 7 | [![Coverage](http://img.shields.io/codecov/c/github/imkira/go-libav.svg?style=flat)](https://codecov.io/github/imkira/go-libav) 8 | 9 | [Go](https://golang.org) language bindings for [ffmpeg](https://ffmpeg.org) 10 | libraries. 11 | 12 | This is still a work in progress. This package still lacks a lot of the libav's 13 | functionality. Please expect many additions/changes in the future. 14 | 15 | # Why 16 | 17 | I am aware of other Go language bindings for ffmpeg. 18 | The reason I decided to build go-libav was because I wanted to have: 19 | 20 | - A more Object-Oriented Programming approach. 21 | - A more Go-like approach to error handling. 22 | - Easier garbage collection. 23 | 24 | # Installation 25 | 26 | First, install ffmpeg 3.x libraries on your system. 27 | 28 | If you need ffmpeg2.x support, use 29 | [ffmpeg2](https://github.com/imkira/go-libav/tree/ffmpeg2) branch (deprecated). 30 | 31 | Then, open the terminal and install the following packages: 32 | 33 | ``` 34 | go get -u github.com/imkira/go-libav/avcodec 35 | go get -u github.com/imkira/go-libav/avfilter 36 | go get -u github.com/imkira/go-libav/avformat 37 | go get -u github.com/imkira/go-libav/avutil 38 | ``` 39 | 40 | # Documentation 41 | 42 | For advanced usage, make sure to check the following documentation: 43 | 44 | - [avcodec](http://godoc.org/github.com/imkira/go-libav/avcodec) 45 | - [avfilter](http://godoc.org/github.com/imkira/go-libav/avfilter) 46 | - [avformat](http://godoc.org/github.com/imkira/go-libav/avformat) 47 | - [avutil](http://godoc.org/github.com/imkira/go-libav/avutil) 48 | 49 | # Examples 50 | 51 | Please check [here for examples](https://github.com/imkira/go-libav/tree/master/examples). 52 | 53 | # FFmpeg versions 54 | 55 | This library supports multiple versions of FFmpeg 3.x, to build, use 56 | 57 | ``` 58 | go build -tags ffmpeg33 59 | go test -tags ffmpeg33 60 | go run -tags ffmpeg33 examples/mediainfo/mediainfo.go 61 | ``` 62 | 63 | Use `ffmpeg30` for FFmpeg 3.0 API, `ffmpeg33` for FFmpeg 3.3 API. 64 | 65 | # Contribute 66 | 67 | Found a bug? Want to contribute and add a new feature? 68 | 69 | Please fork this project and send me a pull request! 70 | 71 | # License 72 | 73 | go-libav is licensed under the MIT license: 74 | 75 | www.opensource.org/licenses/MIT 76 | 77 | # Copyright 78 | 79 | Copyright (c) 2015 Mario Freitas. See 80 | [LICENSE](http://github.com/imkira/go-libav/blob/master/LICENSE) 81 | for further details. 82 | -------------------------------------------------------------------------------- /avcodec/avcodec30.go: -------------------------------------------------------------------------------- 1 | // +build ffmpeg30 2 | 3 | package avcodec 4 | 5 | //#include 6 | //#include 7 | // 8 | // #cgo pkg-config: libavcodec libavutil 9 | import "C" 10 | 11 | import ( 12 | "unsafe" 13 | 14 | "github.com/imkira/go-libav/avutil" 15 | ) 16 | 17 | func (ctx *Context) CopyTo(dst *Context) error { 18 | code := C.avcodec_copy_context(dst.CAVCodecContext, ctx.CAVCodecContext) 19 | if code < 0 { 20 | return avutil.NewErrorFromCode(avutil.ErrorCode(code)) 21 | } 22 | return nil 23 | } 24 | 25 | func (ctx *Context) DecodeVideo(pkt *Packet, frame *avutil.Frame) (bool, int, error) { 26 | var cGotFrame C.int 27 | cFrame := (*C.AVFrame)(unsafe.Pointer(frame.CAVFrame)) 28 | cPkt := (*C.AVPacket)(unsafe.Pointer(pkt.CAVPacket)) 29 | code := C.avcodec_decode_video2(ctx.CAVCodecContext, cFrame, &cGotFrame, cPkt) 30 | var err error 31 | if code < 0 { 32 | err = avutil.NewErrorFromCode(avutil.ErrorCode(code)) 33 | code = 0 34 | } 35 | return (cGotFrame != (C.int)(0)), int(code), err 36 | } 37 | 38 | func (ctx *Context) DecodeAudio(pkt *Packet, frame *avutil.Frame) (bool, int, error) { 39 | var cGotFrame C.int 40 | cFrame := (*C.AVFrame)(unsafe.Pointer(frame.CAVFrame)) 41 | cPkt := (*C.AVPacket)(unsafe.Pointer(pkt.CAVPacket)) 42 | code := C.avcodec_decode_audio4(ctx.CAVCodecContext, cFrame, &cGotFrame, cPkt) 43 | var err error 44 | if code < 0 { 45 | err = avutil.NewErrorFromCode(avutil.ErrorCode(code)) 46 | code = 0 47 | } 48 | return (cGotFrame != (C.int)(0)), int(code), err 49 | } 50 | 51 | func (ctx *Context) EncodeVideo(pkt *Packet, frame *avutil.Frame) (bool, error) { 52 | var cGotFrame C.int 53 | var cFrame *C.AVFrame 54 | if frame != nil { 55 | cFrame = (*C.AVFrame)(unsafe.Pointer(frame.CAVFrame)) 56 | } 57 | cPkt := (*C.AVPacket)(unsafe.Pointer(pkt.CAVPacket)) 58 | code := C.avcodec_encode_video2(ctx.CAVCodecContext, cPkt, cFrame, &cGotFrame) 59 | var err error 60 | if code < 0 { 61 | err = avutil.NewErrorFromCode(avutil.ErrorCode(code)) 62 | } 63 | return (cGotFrame != (C.int)(0)), err 64 | } 65 | 66 | func (ctx *Context) EncodeAudio(pkt *Packet, frame *avutil.Frame) (bool, error) { 67 | var cGotFrame C.int 68 | var cFrame *C.AVFrame 69 | if frame != nil { 70 | cFrame = (*C.AVFrame)(unsafe.Pointer(frame.CAVFrame)) 71 | } 72 | cPkt := (*C.AVPacket)(unsafe.Pointer(pkt.CAVPacket)) 73 | code := C.avcodec_encode_audio2(ctx.CAVCodecContext, cPkt, cFrame, &cGotFrame) 74 | var err error 75 | if code < 0 { 76 | err = avutil.NewErrorFromCode(avutil.ErrorCode(code)) 77 | } 78 | return (cGotFrame != (C.int)(0)), err 79 | } 80 | 81 | func (pkt *Packet) SplitSideData() error { 82 | code := C.av_packet_split_side_data(pkt.CAVPacket) 83 | if code < 0 { 84 | return avutil.NewErrorFromCode(avutil.ErrorCode(code)) 85 | } 86 | return nil 87 | } 88 | 89 | type BitStreamFilterContext struct { 90 | CAVBitStreamFilterContext *C.AVBitStreamFilterContext 91 | } 92 | 93 | func NewBitStreamFilterContextFromC(cCtx unsafe.Pointer) *BitStreamFilterContext { 94 | return &BitStreamFilterContext{CAVBitStreamFilterContext: (*C.AVBitStreamFilterContext)(cCtx)} 95 | } 96 | 97 | func NewBitStreamFilterContextFromName(name string) (*BitStreamFilterContext, error) { 98 | cName := C.CString(name) 99 | defer C.free(unsafe.Pointer(cName)) 100 | cCtx := C.av_bitstream_filter_init(cName) 101 | if cCtx == nil { 102 | return nil, ErrBitStreamFilterNotFound 103 | } 104 | return NewBitStreamFilterContextFromC(unsafe.Pointer(cCtx)), nil 105 | } 106 | 107 | func (ctx *BitStreamFilterContext) Close() { 108 | if ctx.CAVBitStreamFilterContext != nil { 109 | C.av_bitstream_filter_close(ctx.CAVBitStreamFilterContext) 110 | ctx.CAVBitStreamFilterContext = nil 111 | } 112 | } 113 | 114 | func (ctx *BitStreamFilterContext) Next() *BitStreamFilterContext { 115 | next := ctx.CAVBitStreamFilterContext.next 116 | if next == nil { 117 | return nil 118 | } 119 | return NewBitStreamFilterContextFromC(unsafe.Pointer(next)) 120 | } 121 | 122 | func (ctx *BitStreamFilterContext) SetNext(next *BitStreamFilterContext) { 123 | ctx.CAVBitStreamFilterContext.next = next.CAVBitStreamFilterContext 124 | } 125 | 126 | func (ctx *BitStreamFilterContext) Args() string { 127 | args, _ := ctx.ArgsOK() 128 | return args 129 | } 130 | 131 | func (ctx *BitStreamFilterContext) ArgsOK() (string, bool) { 132 | return cStringToStringOk(ctx.CAVBitStreamFilterContext.args) 133 | } 134 | 135 | func (ctx *BitStreamFilterContext) SetArgs(args *string) error { 136 | C.av_freep(unsafe.Pointer(&ctx.CAVBitStreamFilterContext.args)) 137 | if args == nil { 138 | return nil 139 | } 140 | bArgs := []byte(*args) 141 | length := len(bArgs) 142 | cArgs := (*C.char)(C.av_malloc(C.size_t(length + 1))) 143 | if cArgs == nil { 144 | return ErrAllocationError 145 | } 146 | if length > 0 { 147 | C.memcpy(unsafe.Pointer(cArgs), unsafe.Pointer(&bArgs[0]), C.size_t(length)) 148 | } 149 | C.memset(unsafe.Pointer(uintptr(unsafe.Pointer(cArgs))+uintptr(length)), 0, 1) 150 | ctx.CAVBitStreamFilterContext.args = cArgs 151 | return nil 152 | } 153 | -------------------------------------------------------------------------------- /avcodec/avcodec30_test.go: -------------------------------------------------------------------------------- 1 | // +build ffmpeg30 2 | 3 | package avcodec 4 | 5 | import ( 6 | "reflect" 7 | "testing" 8 | 9 | "github.com/imkira/go-libav/avutil" 10 | ) 11 | 12 | func TestNewBitStreamFilterContextFromName(t *testing.T) { 13 | ctx, err := NewBitStreamFilterContextFromName("invalid") 14 | if err != ErrBitStreamFilterNotFound { 15 | t.Fatalf("[NewBitStreamFilterContextFromName] err=%v, NG expected=%v", err, ErrBitStreamFilterNotFound) 16 | } 17 | if ctx != nil { 18 | t.Fatalf("[NewBitStreamFilterContextFromName] ctx=%v, NG expected is nil", ctx) 19 | } 20 | ctx, err = NewBitStreamFilterContextFromName("h264_mp4toannexb") 21 | if err != nil { 22 | t.Fatalf("[NewBitStreamFilterContextFromName] err=%v, NG expected not error", err) 23 | } 24 | if ctx == nil { 25 | t.Fatalf("[NewBitStreamFilterContextFromName] ctx is nil, NG expected is not nil") 26 | } 27 | ctx.Close() 28 | } 29 | 30 | func TestBitStreamFilterContext_Next(t *testing.T) { 31 | ctx, err := NewBitStreamFilterContextFromName("h264_mp4toannexb") 32 | if err != nil { 33 | t.Fatal(err) 34 | } 35 | defer ctx.Close() 36 | result := ctx.Next() 37 | if result != nil { 38 | t.Fatalf("[TestBitStreamFilterContext_Next] result=%v, NG expected nil", result) 39 | } 40 | 41 | next, err := NewBitStreamFilterContextFromName("mjpeg2jpeg") 42 | if err != nil { 43 | t.Fatal(err) 44 | } 45 | defer next.Close() 46 | ctx.SetNext(next) 47 | result = ctx.Next() 48 | if !reflect.DeepEqual(next, result) { 49 | t.Fatalf("[TestBitStreamFilterContext_Next] next=%p, getNext=%p, NG expected same", next, result) 50 | } 51 | } 52 | 53 | func TestBitStreamFilterContext_Args(t *testing.T) { 54 | ctx, err := NewBitStreamFilterContextFromName("h264_mp4toannexb") 55 | if err != nil { 56 | t.Fatal(err) 57 | } 58 | defer ctx.Close() 59 | 60 | _, ok := ctx.ArgsOK() 61 | if ok { 62 | t.Fatalf("[TestBitStreamFilterContext_Args] ok=%t, NG expected=%t", ok, false) 63 | } 64 | result := ctx.Args() 65 | if result != "" { 66 | t.Fatalf("[TestBitStreamFilterContext_Args] result=%s, NG expected blank", result) 67 | } 68 | 69 | input := avutil.String("argstest") 70 | if err := ctx.SetArgs(input); err != nil { 71 | t.Fatalf("[TestBitStreamFilterContext_Args] err=%v, NG expected not error", err) 72 | } 73 | _, ok = ctx.ArgsOK() 74 | if !ok { 75 | t.Fatalf("[TestBitStreamFilterContext_Args] ok=%t, NG expected=%t", ok, true) 76 | } 77 | result = ctx.Args() 78 | if result != *input { 79 | t.Fatalf("[TestBitStreamFilterContext_Args] result=%s, NG expected=%s", result, *input) 80 | } 81 | 82 | if err := ctx.SetArgs(nil); err != nil { 83 | t.Fatalf("[TestBitStreamFilterContext_Args] err=%v, NG expected not error", err) 84 | } 85 | _, ok = ctx.ArgsOK() 86 | if ok { 87 | t.Fatalf("[TestBitStreamFilterContext_Args] ok=%t, NG expected=%t", ok, false) 88 | } 89 | result = ctx.Args() 90 | if result != "" { 91 | t.Fatalf("[TestBitStreamFilterContext_Args] result=%s, NG expected blank", result) 92 | } 93 | } 94 | 95 | func TestBitStreamFilterContext_CloseAll1M(t *testing.T) { 96 | before := testMemoryUsed(t) 97 | for i := 0; i < 1000000; i++ { 98 | ctx, err := NewBitStreamFilterContextFromName("h264_mp4toannexb") 99 | if err != nil { 100 | t.Fatal(err) 101 | } 102 | ctx.Close() 103 | } 104 | testMemoryLeak(t, before, 50*1024*1024) 105 | } 106 | -------------------------------------------------------------------------------- /avcodec/avcodec33.go: -------------------------------------------------------------------------------- 1 | // +build ffmpeg33 2 | 3 | package avcodec 4 | 5 | //#include 6 | //#include 7 | // 8 | // #cgo pkg-config: libavcodec libavutil 9 | import "C" 10 | 11 | import ( 12 | "unsafe" 13 | 14 | "github.com/imkira/go-libav/avutil" 15 | ) 16 | 17 | type CodecParameters struct { 18 | CAVCodecParameters *C.AVCodecParameters 19 | } 20 | 21 | func NewCodecParameters() (*CodecParameters, error) { 22 | cPkt := (*C.AVCodecParameters)(C.avcodec_parameters_alloc()) 23 | if cPkt == nil { 24 | return nil, ErrAllocationError 25 | } 26 | return NewCodecParametersFromC(unsafe.Pointer(cPkt)), nil 27 | } 28 | 29 | func NewCodecParametersFromC(cPSD unsafe.Pointer) *CodecParameters { 30 | return &CodecParameters{CAVCodecParameters: (*C.AVCodecParameters)(cPSD)} 31 | } 32 | 33 | func (cParams *CodecParameters) Free() { 34 | C.avcodec_parameters_free(&cParams.CAVCodecParameters) 35 | } 36 | 37 | func (ctx *Context) CopyTo(dst *Context) error { 38 | // added in lavc 57.33.100 39 | parameters, err := NewCodecParameters() 40 | if err != nil { 41 | return err 42 | } 43 | defer parameters.Free() 44 | cParams := (*C.AVCodecParameters)(unsafe.Pointer(parameters.CAVCodecParameters)) 45 | code := C.avcodec_parameters_from_context(cParams, ctx.CAVCodecContext) 46 | if code < 0 { 47 | return avutil.NewErrorFromCode(avutil.ErrorCode(code)) 48 | } 49 | code = C.avcodec_parameters_to_context(dst.CAVCodecContext, cParams) 50 | if code < 0 { 51 | return avutil.NewErrorFromCode(avutil.ErrorCode(code)) 52 | } 53 | return nil 54 | } 55 | 56 | func (ctx *Context) DecodeVideo(pkt *Packet, frame *avutil.Frame) (bool, int, error) { 57 | cFrame := (*C.AVFrame)(unsafe.Pointer(frame.CAVFrame)) 58 | cPkt := (*C.AVPacket)(unsafe.Pointer(pkt.CAVPacket)) 59 | C.avcodec_send_packet(ctx.CAVCodecContext, cPkt) 60 | code := C.avcodec_receive_frame(ctx.CAVCodecContext, cFrame) 61 | var err error 62 | if code < 0 { 63 | err = avutil.NewErrorFromCode(avutil.ErrorCode(code)) 64 | code = 0 65 | } 66 | return code == 0, int(code), err 67 | } 68 | 69 | func (ctx *Context) DecodeAudio(pkt *Packet, frame *avutil.Frame) (bool, int, error) { 70 | cFrame := (*C.AVFrame)(unsafe.Pointer(frame.CAVFrame)) 71 | cPkt := (*C.AVPacket)(unsafe.Pointer(pkt.CAVPacket)) 72 | C.avcodec_send_packet(ctx.CAVCodecContext, cPkt) 73 | code := C.avcodec_receive_frame(ctx.CAVCodecContext, cFrame) 74 | var err error 75 | if code < 0 { 76 | err = avutil.NewErrorFromCode(avutil.ErrorCode(code)) 77 | code = 0 78 | } 79 | return code == 0, int(code), err 80 | } 81 | 82 | func (ctx *Context) EncodeVideo(pkt *Packet, frame *avutil.Frame) (bool, error) { 83 | var cGotFrame C.int 84 | var cFrame *C.AVFrame 85 | if frame != nil { 86 | cFrame = (*C.AVFrame)(unsafe.Pointer(frame.CAVFrame)) 87 | } 88 | cPkt := (*C.AVPacket)(unsafe.Pointer(pkt.CAVPacket)) 89 | code := C.avcodec_send_frame(ctx.CAVCodecContext, cFrame) 90 | C.avcodec_receive_packet(ctx.CAVCodecContext, cPkt) 91 | var err error 92 | if code < 0 { 93 | err = avutil.NewErrorFromCode(avutil.ErrorCode(code)) 94 | } 95 | return (cGotFrame != (C.int)(0)), err 96 | } 97 | 98 | func (ctx *Context) EncodeAudio(pkt *Packet, frame *avutil.Frame) (bool, error) { 99 | var cGotFrame C.int 100 | var cFrame *C.AVFrame 101 | if frame != nil { 102 | cFrame = (*C.AVFrame)(unsafe.Pointer(frame.CAVFrame)) 103 | } 104 | cPkt := (*C.AVPacket)(unsafe.Pointer(pkt.CAVPacket)) 105 | code := C.avcodec_send_frame(ctx.CAVCodecContext, cFrame) 106 | C.avcodec_receive_packet(ctx.CAVCodecContext, cPkt) 107 | var err error 108 | if code < 0 { 109 | err = avutil.NewErrorFromCode(avutil.ErrorCode(code)) 110 | } 111 | return (cGotFrame != (C.int)(0)), err 112 | } 113 | -------------------------------------------------------------------------------- /avcodec/avcodec_test.go: -------------------------------------------------------------------------------- 1 | package avcodec 2 | 3 | import ( 4 | "bytes" 5 | "log" 6 | "os" 7 | "testing" 8 | 9 | "github.com/imkira/go-libav/avutil" 10 | "github.com/shirou/gopsutil/process" 11 | ) 12 | 13 | func hasVersion(wantMajor, wantMinor int) bool { 14 | gotMajor, gotMinor, _ := Version() 15 | if gotMajor > wantMajor { 16 | return true 17 | } 18 | if gotMajor == wantMajor && gotMinor >= wantMinor { 19 | return true 20 | } 21 | return false 22 | } 23 | 24 | func TestVersion(t *testing.T) { 25 | major, minor, micro := Version() 26 | if major < 57 || minor < 0 || micro < 0 { 27 | t.Fatalf("Invalid version") 28 | } 29 | } 30 | 31 | func TestNewPacket(t *testing.T) { 32 | pkt, err := NewPacket() 33 | if err != nil { 34 | t.Fatal(err) 35 | } 36 | defer pkt.Free() 37 | if pkt == nil { 38 | t.Fatalf("Expecting packet") 39 | } 40 | } 41 | 42 | func TestPacketFree(t *testing.T) { 43 | pkt, _ := NewPacket() 44 | if pkt.CAVPacket == nil { 45 | t.Fatalf("Expecting packet") 46 | } 47 | for i := 0; i < 3; i++ { 48 | pkt.Free() 49 | if pkt.CAVPacket != nil { 50 | t.Fatalf("Not expecting packet") 51 | } 52 | } 53 | } 54 | 55 | func TestPacketDuration(t *testing.T) { 56 | pkt, _ := NewPacket() 57 | defer pkt.Free() 58 | data := int64(100000) 59 | pkt.SetDuration(data) 60 | if pkt.Duration() != data { 61 | t.Fatalf("packet duration expected:%d, got:%d", data, pkt.Duration()) 62 | } 63 | } 64 | 65 | func TestPacketNewFreeLeak10M(t *testing.T) { 66 | before := testMemoryUsed(t) 67 | for i := 0; i < 10000000; i++ { 68 | pkt, err := NewPacket() 69 | if err != nil { 70 | t.Fatal(err) 71 | } 72 | pkt.Free() 73 | } 74 | testMemoryLeak(t, before, 50*1024*1024) 75 | } 76 | 77 | func TestNewContextFromC(t *testing.T) { 78 | ctx := NewContextFromC(nil) 79 | if ctx == nil { 80 | t.Fatalf("Expecting context") 81 | } 82 | } 83 | 84 | func TestNewCodecDescriptorFromC(t *testing.T) { 85 | ctx := NewCodecDescriptorFromC(nil) 86 | if ctx == nil { 87 | t.Fatalf("Expecting context") 88 | } 89 | } 90 | 91 | func TestCodecDescriptor_Params(t *testing.T) { 92 | desc := CodecDescriptorByName("gif") 93 | if desc.ID() != 98 { 94 | t.Fatal("not match ID") 95 | } 96 | if desc.CodecType() != avutil.MediaTypeVideo { 97 | t.Fatal("not match CodecType") 98 | } 99 | if desc.Name() != "gif" { 100 | t.Fatal("not match Name") 101 | } 102 | if desc.LongName() != "GIF (Graphics Interchange Format)" { 103 | t.Fatal("not match LongName") 104 | } 105 | if desc.Props() != CodecPropLossless { 106 | t.Fatal("not match Props") 107 | } 108 | if desc.MimeTypes()[0] != "image/gif" { 109 | t.Fatal("not match MimeTypes") 110 | } 111 | 112 | desc = CodecDescriptorByName("sonic") 113 | if desc.Props() > 0 { 114 | t.Fatal("not match Props") 115 | } 116 | if len(desc.MimeTypes()) > 0 { 117 | t.Fatal("not match MimeTypes") 118 | } 119 | } 120 | 121 | func TestCodecDescriptorByID(t *testing.T) { 122 | found := CodecDescriptorByID(CodecID(28)) 123 | if found == nil { 124 | t.Fatal("not found") 125 | } 126 | notFound := CodecDescriptorByID(CodecID(0)) 127 | if notFound != nil { 128 | t.Fatal("found") 129 | } 130 | } 131 | 132 | func TestCodecDescriptorByName(t *testing.T) { 133 | found := CodecDescriptorByName("h264") 134 | if found == nil { 135 | t.Fatal("not found") 136 | } 137 | notFound := CodecDescriptorByName("notfound") 138 | if notFound != nil { 139 | t.Fatal("found") 140 | } 141 | } 142 | 143 | func TestCodecDescriptors(t *testing.T) { 144 | descriptors := CodecDescriptors() 145 | if len(descriptors) == 0 { 146 | t.Fatal("not found") 147 | } 148 | } 149 | 150 | func TestCodecDescriptor_Profiles(t *testing.T) { 151 | type data struct { 152 | id int 153 | name string 154 | } 155 | datas := []*data{ 156 | {id: 66, name: "Baseline"}, 157 | {id: 578, name: "Constrained Baseline"}, 158 | {id: 77, name: "Main"}, 159 | {id: 88, name: "Extended"}, 160 | {id: 100, name: "High"}, 161 | {id: 110, name: "High 10"}, 162 | {id: 2158, name: "High 10 Intra"}, 163 | {id: 122, name: "High 4:2:2"}, 164 | {id: 2170, name: "High 4:2:2 Intra"}, 165 | {id: 144, name: "High 4:4:4"}, 166 | {id: 244, name: "High 4:4:4 Predictive"}, 167 | {id: 2292, name: "High 4:4:4 Intra"}, 168 | {id: 44, name: "CAVLC 4:4:4"}, 169 | } 170 | if hasVersion(57, 50) { 171 | // added in avcodec 57.50.100 172 | datas = append(datas, []*data{ 173 | {id: 118, name: "Multiview High"}, 174 | {id: 128, name: "Stereo High"}, 175 | }...) 176 | } 177 | 178 | desc := CodecDescriptorByName("h264") 179 | if desc == nil { 180 | t.Fatal("not found") 181 | } 182 | profiles := desc.Profiles() 183 | if len(datas) != len(profiles) { 184 | log.Println("UNEXPECTED profiles list:") 185 | for _, p := range profiles { 186 | log.Println(p.ID(), p.Name()) 187 | } 188 | t.Fatalf("profiles count expected:%d, got:%d", len(datas), len(profiles)) 189 | } 190 | for i, profile := range profiles { 191 | if datas[i].id != profile.ID() { 192 | t.Errorf("profile id expected:%d, got:%d", datas[i].id, profile.ID()) 193 | } 194 | if datas[i].name != profile.Name() { 195 | t.Errorf("profile name expected:%s, got:%s", datas[i].name, profile.Name()) 196 | } 197 | } 198 | } 199 | 200 | func TestContextStatInOutOK(t *testing.T) { 201 | ctx := testNewContextWithCodec(t, "mpeg4") 202 | codec := FindEncoderByName("mpeg4") 203 | if codec == nil { 204 | t.Error("error") 205 | } 206 | ctx, err := NewContextWithCodec(codec) 207 | if err != nil { 208 | t.Error("error") 209 | } 210 | defer ctx.Free() 211 | 212 | expected := []byte("stats_in") 213 | if err := ctx.SetStatsIn(expected); err != nil { 214 | t.Fatalf("[TestContextStatInOutOK] err=%v NG, expected is not error", err) 215 | } 216 | result := ctx.StatsIn() 217 | if !bytes.Equal(result, expected) { 218 | t.Fatalf("[TestContextStatInOutOK] result=%s NG, expected=%s", result, expected) 219 | } 220 | expected = []byte{} 221 | if err := ctx.SetStatsIn(expected); err != nil { 222 | t.Fatalf("[TestContextStatInOutOK] err=%v NG, expected is not error", err) 223 | } 224 | result = ctx.StatsIn() 225 | if !bytes.Equal(result, expected) { 226 | t.Fatalf("[TestContextStatInOutOK] result=%v NG, expected=%v", result, expected) 227 | } 228 | if err := ctx.SetStatsIn(nil); err != nil { 229 | t.Fatalf("[TestContextStatInOutOK] err=%v NG, expected is not error", err) 230 | } 231 | result = ctx.StatsIn() 232 | if result != nil { 233 | t.Fatalf("[TestContextStatInOutOK] result=%v NG, expected=nil", result) 234 | } 235 | 236 | expected = []byte("stats_out") 237 | if err := ctx.SetStatsOut(expected); err != nil { 238 | t.Fatalf("[TestContextStatInOutOK] err=%v NG, expected is not error", err) 239 | } 240 | result = ctx.StatsOut() 241 | if !bytes.Equal(result, expected) { 242 | t.Fatalf("[TestContextStatInOutOK] result=%s NG, expected=%s", result, expected) 243 | } 244 | expected = []byte{} 245 | if err := ctx.SetStatsOut(expected); err != nil { 246 | t.Fatalf("[TestContextStatInOutOK] err=%v NG, expected is not error", err) 247 | } 248 | result = ctx.StatsOut() 249 | if !bytes.Equal(result, expected) { 250 | t.Fatalf("[TestContextStatInOutOK] result=%v NG, expected=%v", result, expected) 251 | } 252 | if err := ctx.SetStatsOut(nil); err != nil { 253 | t.Fatalf("[TestContextStatInOutOK] err=%v NG, expected is not error", err) 254 | } 255 | result = ctx.StatsOut() 256 | if result != nil { 257 | t.Fatalf("[TestContextStatInOutOK] result=%v NG, expected=nil", result) 258 | } 259 | } 260 | 261 | func TestCodecProfileName(t *testing.T) { 262 | codec := FindDecoderByName("h264") 263 | if codec == nil { 264 | t.Fatal("codec not found") 265 | } 266 | name := codec.ProfileName(100) 267 | if name != "High" { 268 | t.Errorf("profile name expected:High, got:%s", name) 269 | } 270 | name = codec.ProfileName(1) 271 | if name != "" { 272 | t.Errorf("unexpected profile name, got:%s", name) 273 | } 274 | } 275 | 276 | func TestCodecProfiles(t *testing.T) { 277 | type data struct { 278 | id int 279 | name string 280 | } 281 | datas := []*data{ 282 | {id: 66, name: "Baseline"}, 283 | {id: 578, name: "Constrained Baseline"}, 284 | {id: 77, name: "Main"}, 285 | {id: 88, name: "Extended"}, 286 | {id: 100, name: "High"}, 287 | {id: 110, name: "High 10"}, 288 | {id: 2158, name: "High 10 Intra"}, 289 | {id: 122, name: "High 4:2:2"}, 290 | {id: 2170, name: "High 4:2:2 Intra"}, 291 | {id: 144, name: "High 4:4:4"}, 292 | {id: 244, name: "High 4:4:4 Predictive"}, 293 | {id: 2292, name: "High 4:4:4 Intra"}, 294 | {id: 44, name: "CAVLC 4:4:4"}, 295 | } 296 | if hasVersion(57, 50) { 297 | // added in avcodec 57.50.100 298 | datas = append(datas, []*data{ 299 | {id: 118, name: "Multiview High"}, 300 | {id: 128, name: "Stereo High"}, 301 | }...) 302 | } 303 | 304 | codec := FindDecoderByName("h264") 305 | if codec == nil { 306 | t.Fatal("codec not found") 307 | } 308 | profiles := codec.Profiles() 309 | if len(datas) != len(profiles) { 310 | log.Println("UNEXPECTED profiles list:") 311 | for _, p := range profiles { 312 | log.Println(p.ID(), p.Name()) 313 | } 314 | t.Fatalf("profiles count expected:%d, got:%d", len(datas), len(profiles)) 315 | } 316 | for i, profile := range profiles { 317 | if datas[i].id != profile.ID() { 318 | t.Errorf("profile id expected:%d, got:%d", datas[i].id, profile.ID()) 319 | } 320 | if datas[i].name != profile.Name() { 321 | t.Errorf("profile name expected:%s, got:%s", datas[i].name, profile.Name()) 322 | } 323 | } 324 | } 325 | 326 | func TestContextBitRate(t *testing.T) { 327 | ctx := testNewContextWithCodec(t, "h264") 328 | defer ctx.Free() 329 | data := int64(180) 330 | ctx.SetBitRate(data) 331 | if ctx.BitRate() != data { 332 | t.Fatalf("context bitrate expected:%d, got:%d", data, ctx.BitRate()) 333 | } 334 | } 335 | 336 | func TestContextRCMaxRate(t *testing.T) { 337 | ctx := testNewContextWithCodec(t, "h264") 338 | defer ctx.Free() 339 | data := int64(200) 340 | ctx.SetRCMaxRate(data) 341 | if ctx.RCMaxRate() != data { 342 | t.Fatalf("context rc maxrate expected:%d, got:%d", data, ctx.RCMaxRate()) 343 | } 344 | } 345 | 346 | func TestContextRCMinRate(t *testing.T) { 347 | ctx := testNewContextWithCodec(t, "h264") 348 | defer ctx.Free() 349 | data := int64(50) 350 | ctx.SetRCMinRate(data) 351 | if ctx.RCMinRate() != data { 352 | t.Fatalf("context rc minrate expected:%d, got:%d", data, ctx.RCMinRate()) 353 | } 354 | } 355 | 356 | func TestContextNewFreeLeak1M(t *testing.T) { 357 | before := testMemoryUsed(t) 358 | for i := 0; i < 1000000; i++ { 359 | ctx := testNewContextWithCodec(t, "mpeg4") 360 | ctx.Free() 361 | } 362 | testMemoryLeak(t, before, 50*1024*1024) 363 | } 364 | 365 | func testNewContextWithCodec(t *testing.T, name string) *Context { 366 | codec := FindDecoderByName(name) 367 | if codec == nil { 368 | t.Fatalf("Expecting codec") 369 | } 370 | ctx, err := NewContextWithCodec(codec) 371 | if err != nil { 372 | t.Fatal(err) 373 | } 374 | if ctx == nil { 375 | t.Fatalf("Expecting context") 376 | } 377 | return ctx 378 | } 379 | 380 | func TestNewContextWithCodecNil(t *testing.T) { 381 | ctx, err := NewContextWithCodec(nil) 382 | if err != nil { 383 | t.Fatalf("Expecting allocate") 384 | } 385 | if ctx == nil { 386 | t.Fatalf("Expecting context") 387 | } 388 | } 389 | 390 | func testMemoryUsed(t *testing.T) uint64 { 391 | p, err := process.NewProcess(int32(os.Getpid())) 392 | if err != nil { 393 | t.Fatal(err) 394 | } 395 | info, err := p.MemoryInfo() 396 | if err != nil { 397 | t.Fatal(err) 398 | } 399 | return info.RSS 400 | } 401 | 402 | func testMemoryLeak(t *testing.T, before uint64, diff uint64) { 403 | after := testMemoryUsed(t) 404 | if after > before && after-before > diff { 405 | t.Fatalf("memory leak detected: %d bytes", after-before) 406 | } 407 | } 408 | 409 | func TestFindBestPixelFormat(t *testing.T) { 410 | list := []avutil.PixelFormat{} 411 | src := findPixelFormatByName("rgb48be", t) 412 | expectedBest := avutil.PixelFormatNone 413 | loss := avutil.LossFlagAlpha 414 | best := FindBestPixelFormat(list, src, false) 415 | if best != expectedBest { 416 | t.Fatalf("[TestFindBestPixelFormat] best=%d, NG expected=%d", best, expectedBest) 417 | } 418 | 419 | list = []avutil.PixelFormat{ 420 | findPixelFormatByName("yuv420p", t), 421 | findPixelFormatByName("yuv444p", t), 422 | findPixelFormatByName("yuvj420p", t), 423 | } 424 | expectedBest = findPixelFormatByName("yuv444p", t) 425 | best = FindBestPixelFormat(list, src, false) 426 | if best != expectedBest { 427 | t.Fatalf("[TestFindBestPixelFormat2] best=%d, NG expected=%d", best, expectedBest) 428 | } 429 | 430 | expectedBest = findPixelFormatByName("yuv420p", t) 431 | expectedLoss := avutil.LossFlagResolution + avutil.LossFlagDepth + avutil.LossFlagColorspace 432 | best, retLoss := FindBestPixelFormatWithLossFlags(list, src, false, avutil.LossFlagChroma) 433 | if best != expectedBest { 434 | t.Fatalf("[TestFindBestPixelFormat3] best=%d, NG expected=%d", best, expectedBest) 435 | } 436 | if retLoss != expectedLoss { 437 | t.Fatalf("[TestFindBestPixelFormat3] loss=%d, NG expected=%d", loss, expectedLoss) 438 | } 439 | } 440 | 441 | func findPixelFormatByName(name string, t *testing.T) avutil.PixelFormat { 442 | pixFmt, ok := avutil.FindPixelFormatByName(name) 443 | if !ok { 444 | t.Fatalf("pixel format not found") 445 | } 446 | return pixFmt 447 | } 448 | -------------------------------------------------------------------------------- /avfilter/avfilter.go: -------------------------------------------------------------------------------- 1 | package avfilter 2 | 3 | //#include 4 | //#include 5 | //#include 6 | //#include 7 | //#include 8 | //#include 9 | // 10 | //#ifdef AV_BUFFERSRC_FLAG_NO_COPY 11 | //#define GO_AV_BUFFERSRC_FLAG_NO_COPY AV_BUFFERSRC_FLAG_NO_COPY 12 | //#else 13 | //#define GO_AV_BUFFERSRC_FLAG_NO_COPY 0 14 | //#endif 15 | // 16 | //static const AVFilterLink *go_av_links_get(const AVFilterLink **links, unsigned int n) 17 | //{ 18 | // return links[n]; 19 | //} 20 | // 21 | //static const int GO_AVERROR(int e) 22 | //{ 23 | // return AVERROR(e); 24 | //} 25 | // 26 | // int GO_AVFILTER_VERSION_MAJOR = LIBAVFILTER_VERSION_MAJOR; 27 | // int GO_AVFILTER_VERSION_MINOR = LIBAVFILTER_VERSION_MINOR; 28 | // int GO_AVFILTER_VERSION_MICRO = LIBAVFILTER_VERSION_MICRO; 29 | // 30 | //#define GO_AVFILTER_AUTO_CONVERT_ALL ((unsigned)AVFILTER_AUTO_CONVERT_ALL) 31 | //#define GO_AVFILTER_AUTO_CONVERT_NONE ((unsigned)AVFILTER_AUTO_CONVERT_NONE) 32 | // 33 | //#cgo pkg-config: libavfilter libavutil 34 | import "C" 35 | 36 | import ( 37 | "errors" 38 | "unsafe" 39 | 40 | "github.com/imkira/go-libav/avutil" 41 | ) 42 | 43 | var ( 44 | ErrAllocationError = errors.New("allocation error") 45 | ) 46 | 47 | type Flags int 48 | 49 | const ( 50 | FlagDynamicInputs Flags = C.AVFILTER_FLAG_DYNAMIC_INPUTS 51 | FlagDynamicOutputs Flags = C.AVFILTER_FLAG_DYNAMIC_OUTPUTS 52 | FlagSliceThreads Flags = C.AVFILTER_FLAG_SLICE_THREADS 53 | FlagSupportTimelineGeneric Flags = C.AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC 54 | FlagSupportTimelineInternal Flags = C.AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL 55 | FlagSupportTimeline Flags = C.AVFILTER_FLAG_SUPPORT_TIMELINE 56 | ) 57 | 58 | type BufferSrcFlags C.int 59 | 60 | const ( 61 | BufferSrcFlagNoCheckFormat BufferSrcFlags = C.AV_BUFFERSRC_FLAG_NO_CHECK_FORMAT 62 | BufferSrcFlagNoCopy BufferSrcFlags = C.GO_AV_BUFFERSRC_FLAG_NO_COPY 63 | BufferSrcFlagPush BufferSrcFlags = C.AV_BUFFERSRC_FLAG_PUSH 64 | BufferSrcFlagKeepRef BufferSrcFlags = C.AV_BUFFERSRC_FLAG_KEEP_REF 65 | ) 66 | 67 | type GraphAutoConvertFlags uint 68 | 69 | const ( 70 | GraphAutoConvertFlagAll GraphAutoConvertFlags = C.GO_AVFILTER_AUTO_CONVERT_ALL 71 | GraphAutoConvertFlagNone GraphAutoConvertFlags = C.GO_AVFILTER_AUTO_CONVERT_NONE 72 | ) 73 | 74 | func init() { 75 | RegisterAll() 76 | } 77 | 78 | func Version() (int, int, int) { 79 | return int(C.GO_AVFILTER_VERSION_MAJOR), int(C.GO_AVFILTER_VERSION_MINOR), int(C.GO_AVFILTER_VERSION_MICRO) 80 | } 81 | 82 | func RegisterAll() { 83 | C.avfilter_register_all() 84 | } 85 | 86 | type Filter struct { 87 | CAVFilter *C.AVFilter 88 | } 89 | 90 | func NewFilterFromC(cFilter unsafe.Pointer) *Filter { 91 | return &Filter{CAVFilter: (*C.AVFilter)(cFilter)} 92 | } 93 | 94 | func (f *Filter) Name() string { 95 | str, _ := f.NameOk() 96 | return str 97 | } 98 | 99 | func (f *Filter) NameOk() (string, bool) { 100 | return cStringToStringOk(f.CAVFilter.name) 101 | } 102 | 103 | func (f *Filter) Description() string { 104 | str, _ := f.DescriptionOk() 105 | return str 106 | } 107 | 108 | func (f *Filter) DescriptionOk() (string, bool) { 109 | return cStringToStringOk(f.CAVFilter.description) 110 | } 111 | 112 | func (f *Filter) PrivateClass() *avutil.Class { 113 | if f.CAVFilter.priv_class == nil { 114 | return nil 115 | } 116 | return avutil.NewClassFromC(unsafe.Pointer(f.CAVFilter.priv_class)) 117 | } 118 | 119 | func (f *Filter) Flags() Flags { 120 | return Flags(f.CAVFilter.flags) 121 | } 122 | 123 | func Filters() []*Filter { 124 | var filters []*Filter 125 | var cPrev *C.AVFilter 126 | for { 127 | if cPrev = C.avfilter_next(cPrev); cPrev == nil { 128 | break 129 | } 130 | filters = append(filters, NewFilterFromC(unsafe.Pointer(cPrev))) 131 | } 132 | return filters 133 | } 134 | 135 | func FindFilterByName(name string) *Filter { 136 | cName := C.CString(name) 137 | defer C.free(unsafe.Pointer(cName)) 138 | cFilter := C.avfilter_get_by_name(cName) 139 | if cFilter == nil { 140 | return nil 141 | } 142 | return NewFilterFromC(unsafe.Pointer(cFilter)) 143 | } 144 | 145 | type Link struct { 146 | CAVFilterLink *C.AVFilterLink 147 | } 148 | 149 | func NewLinkFromC(cLink unsafe.Pointer) *Link { 150 | return &Link{CAVFilterLink: (*C.AVFilterLink)(cLink)} 151 | } 152 | 153 | func (l *Link) Src() *Context { 154 | cContext := l.CAVFilterLink.src 155 | if cContext == nil { 156 | return nil 157 | } 158 | return NewContextFromC(unsafe.Pointer(cContext)) 159 | } 160 | 161 | func (l *Link) Dst() *Context { 162 | cContext := l.CAVFilterLink.dst 163 | if cContext == nil { 164 | return nil 165 | } 166 | return NewContextFromC(unsafe.Pointer(cContext)) 167 | } 168 | 169 | func (l *Link) Type() avutil.MediaType { 170 | return (avutil.MediaType)(l.CAVFilterLink._type) 171 | } 172 | 173 | func (l *Link) Width() int { 174 | return int(l.CAVFilterLink.w) 175 | } 176 | 177 | func (l *Link) Height() int { 178 | return int(l.CAVFilterLink.h) 179 | } 180 | 181 | func (l *Link) SampleAspectRatio() *avutil.Rational { 182 | return avutil.NewRationalFromC(unsafe.Pointer(&l.CAVFilterLink.sample_aspect_ratio)) 183 | } 184 | 185 | func (l *Link) ChannelLayout() avutil.ChannelLayout { 186 | return (avutil.ChannelLayout)(l.CAVFilterLink.channel_layout) 187 | } 188 | 189 | func (l *Link) SampleRate() int { 190 | return int(l.CAVFilterLink.sample_rate) 191 | } 192 | 193 | func (l *Link) Format() int { 194 | return int(l.CAVFilterLink.format) 195 | } 196 | 197 | func (l *Link) PixelFormat() avutil.PixelFormat { 198 | return avutil.PixelFormat(l.CAVFilterLink.format) 199 | } 200 | 201 | func (l *Link) SampleFormat() avutil.SampleFormat { 202 | return avutil.SampleFormat(l.CAVFilterLink.format) 203 | } 204 | 205 | func (l *Link) TimeBase() *avutil.Rational { 206 | return avutil.NewRationalFromC(unsafe.Pointer(&l.CAVFilterLink.time_base)) 207 | } 208 | 209 | func (l *Link) RequestSamples() int { 210 | return int(l.CAVFilterLink.request_samples) 211 | } 212 | 213 | func (l *Link) CurrentPTS() int64 { 214 | return int64(l.CAVFilterLink.current_pts) 215 | } 216 | 217 | func (l *Link) AgeIndex() int { 218 | return int(l.CAVFilterLink.age_index) 219 | } 220 | 221 | func (l *Link) FrameRate() *avutil.Rational { 222 | return avutil.NewRationalFromC(unsafe.Pointer(&l.CAVFilterLink.frame_rate)) 223 | } 224 | 225 | func (l *Link) MinSamples() int { 226 | return int(l.CAVFilterLink.min_samples) 227 | } 228 | 229 | func (l *Link) MaxSamples() int { 230 | return int(l.CAVFilterLink.max_samples) 231 | } 232 | 233 | func (l *Link) Channels() int { 234 | return int(C.avfilter_link_get_channels(l.CAVFilterLink)) 235 | } 236 | 237 | type Context struct { 238 | CAVFilterContext *C.AVFilterContext 239 | *avutil.OptionAccessor 240 | } 241 | 242 | func NewContextFromC(cCtx unsafe.Pointer) *Context { 243 | return &Context{ 244 | CAVFilterContext: (*C.AVFilterContext)(cCtx), 245 | OptionAccessor: avutil.NewOptionAccessor(cCtx, false), 246 | } 247 | } 248 | 249 | func (ctx *Context) Init() error { 250 | options := avutil.NewDictionary() 251 | defer options.Free() 252 | return ctx.InitWithDictionary(options) 253 | } 254 | 255 | func (ctx *Context) InitWithString(args string) error { 256 | cArgs := C.CString(args) 257 | defer C.free(unsafe.Pointer(cArgs)) 258 | code := C.avfilter_init_str(ctx.CAVFilterContext, cArgs) 259 | if code < 0 { 260 | return avutil.NewErrorFromCode(avutil.ErrorCode(code)) 261 | } 262 | return nil 263 | } 264 | 265 | func (ctx *Context) InitWithDictionary(options *avutil.Dictionary) error { 266 | var cOptions **C.AVDictionary 267 | if options != nil { 268 | cOptions = (**C.AVDictionary)(options.Pointer()) 269 | } 270 | code := C.avfilter_init_dict(ctx.CAVFilterContext, cOptions) 271 | if code < 0 { 272 | return avutil.NewErrorFromCode(avutil.ErrorCode(code)) 273 | } 274 | return nil 275 | } 276 | 277 | func (ctx *Context) Link(srcPad uint, dst *Context, dstPad uint) error { 278 | cSrc := ctx.CAVFilterContext 279 | cDst := dst.CAVFilterContext 280 | code := C.avfilter_link(cSrc, C.uint(srcPad), cDst, C.uint(dstPad)) 281 | if code < 0 { 282 | return avutil.NewErrorFromCode(avutil.ErrorCode(code)) 283 | } 284 | return nil 285 | } 286 | 287 | func (ctx *Context) AddFrame(frame *avutil.Frame) error { 288 | var cFrame *C.AVFrame 289 | if frame != nil { 290 | cFrame = (*C.AVFrame)(unsafe.Pointer(frame.CAVFrame)) 291 | } 292 | code := C.av_buffersrc_add_frame(ctx.CAVFilterContext, cFrame) 293 | if code < 0 { 294 | return avutil.NewErrorFromCode(avutil.ErrorCode(code)) 295 | } 296 | return nil 297 | } 298 | 299 | func (ctx *Context) AddFrameWithFlags(frame *avutil.Frame, flags BufferSrcFlags) error { 300 | var cFrame *C.AVFrame 301 | if frame != nil { 302 | cFrame = (*C.AVFrame)(unsafe.Pointer(frame.CAVFrame)) 303 | } 304 | code := C.av_buffersrc_add_frame_flags(ctx.CAVFilterContext, cFrame, (C.int)(flags)) 305 | if code < 0 { 306 | return avutil.NewErrorFromCode(avutil.ErrorCode(code)) 307 | } 308 | return nil 309 | } 310 | 311 | func (ctx *Context) WriteFrame(frame *avutil.Frame) error { 312 | var cFrame *C.AVFrame 313 | if frame != nil { 314 | cFrame = (*C.AVFrame)(unsafe.Pointer(frame.CAVFrame)) 315 | } 316 | code := C.av_buffersrc_write_frame(ctx.CAVFilterContext, cFrame) 317 | if code < 0 { 318 | return avutil.NewErrorFromCode(avutil.ErrorCode(code)) 319 | } 320 | return nil 321 | } 322 | 323 | func (ctx *Context) GetFrame(frame *avutil.Frame) (bool, error) { 324 | var cFrame *C.AVFrame 325 | if frame != nil { 326 | cFrame = (*C.AVFrame)(unsafe.Pointer(frame.CAVFrame)) 327 | } 328 | code := C.av_buffersink_get_frame(ctx.CAVFilterContext, cFrame) 329 | if code < 0 { 330 | switch avutil.ErrorCode(code) { 331 | case avutil.ErrorCode(C.GO_AVERROR(C.EAGAIN)): 332 | return false, nil 333 | case avutil.ErrorCodeEOF: 334 | return false, nil 335 | } 336 | return false, avutil.NewErrorFromCode(avutil.ErrorCode(code)) 337 | } 338 | return true, nil 339 | } 340 | 341 | func (ctx *Context) SetFrameSize(size uint) { 342 | C.av_buffersink_set_frame_size(ctx.CAVFilterContext, C.uint(size)) 343 | } 344 | 345 | func (ctx *Context) Class() *avutil.Class { 346 | if ctx.CAVFilterContext.av_class == nil { 347 | return nil 348 | } 349 | return avutil.NewClassFromC(unsafe.Pointer(ctx.CAVFilterContext.av_class)) 350 | } 351 | 352 | func (ctx *Context) Name() string { 353 | str, _ := ctx.NameOk() 354 | return str 355 | } 356 | 357 | func (ctx *Context) NameOk() (string, bool) { 358 | return cStringToStringOk(ctx.CAVFilterContext.name) 359 | } 360 | 361 | func (ctx *Context) Filter() *Filter { 362 | cFilter := ctx.CAVFilterContext.filter 363 | if cFilter == nil { 364 | return nil 365 | } 366 | return NewFilterFromC(unsafe.Pointer(cFilter)) 367 | } 368 | 369 | func (ctx *Context) Inputs() []*Link { 370 | count := ctx.NumberOfInputs() 371 | if count <= 0 { 372 | return nil 373 | } 374 | links := make([]*Link, 0, count) 375 | for i := uint(0); i < count; i++ { 376 | cLink := C.go_av_links_get(ctx.CAVFilterContext.inputs, C.uint(i)) 377 | link := NewLinkFromC(unsafe.Pointer(cLink)) 378 | links = append(links, link) 379 | } 380 | return links 381 | } 382 | 383 | func (ctx *Context) NumberOfInputs() uint { 384 | return uint(ctx.CAVFilterContext.nb_inputs) 385 | } 386 | 387 | func (ctx *Context) Outputs() []*Link { 388 | count := ctx.NumberOfOutputs() 389 | if count <= 0 { 390 | return nil 391 | } 392 | links := make([]*Link, 0, count) 393 | for i := uint(0); i < count; i++ { 394 | cLink := C.go_av_links_get(ctx.CAVFilterContext.outputs, C.uint(i)) 395 | link := NewLinkFromC(unsafe.Pointer(cLink)) 396 | links = append(links, link) 397 | } 398 | return links 399 | } 400 | 401 | func (ctx *Context) NumberOfOutputs() uint { 402 | return uint(ctx.CAVFilterContext.nb_outputs) 403 | } 404 | 405 | func (ctx *Context) FrameRate() *avutil.Rational { 406 | r := C.av_buffersink_get_frame_rate(ctx.CAVFilterContext) 407 | return avutil.NewRationalFromC(unsafe.Pointer(&r)) 408 | } 409 | 410 | type Graph struct { 411 | CAVFilterGraph *C.AVFilterGraph 412 | } 413 | 414 | func NewGraph() (*Graph, error) { 415 | cGraph := C.avfilter_graph_alloc() 416 | if cGraph == nil { 417 | return nil, ErrAllocationError 418 | } 419 | return NewGraphFromC(unsafe.Pointer(cGraph)), nil 420 | } 421 | 422 | func NewGraphFromC(cGraph unsafe.Pointer) *Graph { 423 | return &Graph{CAVFilterGraph: (*C.AVFilterGraph)(cGraph)} 424 | } 425 | 426 | func (g *Graph) Free() { 427 | if g.CAVFilterGraph != nil { 428 | C.avfilter_graph_free(&g.CAVFilterGraph) 429 | } 430 | } 431 | 432 | func (g *Graph) Class() *avutil.Class { 433 | if g.CAVFilterGraph.av_class == nil { 434 | return nil 435 | } 436 | return avutil.NewClassFromC(unsafe.Pointer(g.CAVFilterGraph.av_class)) 437 | } 438 | 439 | func (g *Graph) NumberOfFilters() uint { 440 | return uint(g.CAVFilterGraph.nb_filters) 441 | } 442 | 443 | func (g *Graph) AddFilter(filter *Filter, name string) (*Context, error) { 444 | cName := C.CString(name) 445 | defer C.free(unsafe.Pointer(cName)) 446 | cCtx := C.avfilter_graph_alloc_filter(g.CAVFilterGraph, filter.CAVFilter, cName) 447 | if cCtx == nil { 448 | return nil, ErrAllocationError 449 | } 450 | return NewContextFromC(unsafe.Pointer(cCtx)), nil 451 | } 452 | 453 | func (g *Graph) Parse(filters string, input, output *InOut) error { 454 | cFilters := C.CString(filters) 455 | defer C.free(unsafe.Pointer(cFilters)) 456 | cInput := &input.CAVFilterInOut 457 | cOutput := &output.CAVFilterInOut 458 | code := C.avfilter_graph_parse_ptr(g.CAVFilterGraph, cFilters, cInput, cOutput, nil) 459 | if code < 0 { 460 | return avutil.NewErrorFromCode(avutil.ErrorCode(code)) 461 | } 462 | return nil 463 | } 464 | 465 | func (g *Graph) Config() error { 466 | code := C.avfilter_graph_config(g.CAVFilterGraph, nil) 467 | if code < 0 { 468 | return avutil.NewErrorFromCode(avutil.ErrorCode(code)) 469 | } 470 | return nil 471 | } 472 | 473 | func (g *Graph) RequestOldest() error { 474 | code := C.avfilter_graph_request_oldest(g.CAVFilterGraph) 475 | if code < 0 { 476 | return avutil.NewErrorFromCode(avutil.ErrorCode(code)) 477 | } 478 | return nil 479 | } 480 | 481 | func (g *Graph) Dump() (string, error) { 482 | cStr := C.avfilter_graph_dump(g.CAVFilterGraph, nil) 483 | if cStr == nil { 484 | return "", ErrAllocationError 485 | } 486 | defer C.av_free(unsafe.Pointer(cStr)) 487 | return C.GoString(cStr), nil 488 | } 489 | 490 | func (g *Graph) DumpWithOptions(options string) (string, error) { 491 | cOptions := C.CString(options) 492 | defer C.free(unsafe.Pointer(cOptions)) 493 | cStr := C.avfilter_graph_dump(g.CAVFilterGraph, cOptions) 494 | if cStr == nil { 495 | return "", ErrAllocationError 496 | } 497 | defer C.av_free(unsafe.Pointer(cStr)) 498 | return C.GoString(cStr), nil 499 | } 500 | 501 | func (g *Graph) AutoConvertFlags() GraphAutoConvertFlags { 502 | return GraphAutoConvertFlags(g.CAVFilterGraph.disable_auto_convert) 503 | } 504 | 505 | func (g *Graph) SetAutoConvertFlags(flags GraphAutoConvertFlags) { 506 | C.avfilter_graph_set_auto_convert(g.CAVFilterGraph, (C.uint)(flags)) 507 | } 508 | 509 | type InOut struct { 510 | CAVFilterInOut *C.AVFilterInOut 511 | } 512 | 513 | func NewInOut() (*InOut, error) { 514 | cInOut := C.avfilter_inout_alloc() 515 | if cInOut == nil { 516 | return nil, ErrAllocationError 517 | } 518 | return NewInOutFromC(unsafe.Pointer(cInOut)), nil 519 | } 520 | 521 | func NewInOutFromC(cInOut unsafe.Pointer) *InOut { 522 | return &InOut{CAVFilterInOut: (*C.AVFilterInOut)(cInOut)} 523 | } 524 | 525 | func (io *InOut) Free() { 526 | if io.CAVFilterInOut != nil { 527 | C.avfilter_inout_free(&io.CAVFilterInOut) 528 | } 529 | } 530 | 531 | func (io *InOut) Name() string { 532 | str, _ := io.NameOk() 533 | return str 534 | } 535 | 536 | func (io *InOut) NameOk() (string, bool) { 537 | return cStringToStringOk(io.CAVFilterInOut.name) 538 | } 539 | 540 | func (io *InOut) SetName(name string) error { 541 | C.free(unsafe.Pointer(io.CAVFilterInOut.name)) 542 | io.CAVFilterInOut.name = C.CString(name) 543 | if io.CAVFilterInOut.name == nil { 544 | return ErrAllocationError 545 | } 546 | return nil 547 | } 548 | 549 | func (io *InOut) PadIndex() int { 550 | return int(io.CAVFilterInOut.pad_idx) 551 | } 552 | 553 | func (io *InOut) SetPadIndex(index int) { 554 | io.CAVFilterInOut.pad_idx = (C.int)(index) 555 | } 556 | 557 | func (io *InOut) Context() *Context { 558 | if io.CAVFilterInOut.filter_ctx == nil { 559 | return nil 560 | } 561 | return NewContextFromC(unsafe.Pointer(io.CAVFilterInOut.filter_ctx)) 562 | } 563 | 564 | func (io *InOut) SetContext(ctx *Context) { 565 | var cCtx *C.AVFilterContext 566 | if ctx != nil { 567 | cCtx = ctx.CAVFilterContext 568 | } 569 | io.CAVFilterInOut.filter_ctx = cCtx 570 | } 571 | 572 | func (io *InOut) Next() *InOut { 573 | if io.CAVFilterInOut.next == nil { 574 | return nil 575 | } 576 | return NewInOutFromC(unsafe.Pointer(io.CAVFilterInOut.next)) 577 | } 578 | 579 | func (io *InOut) SetNext(next *InOut) { 580 | var cInOut *C.AVFilterInOut 581 | if next != nil { 582 | cInOut = next.CAVFilterInOut 583 | } 584 | io.CAVFilterInOut.next = cInOut 585 | } 586 | 587 | func cStringToStringOk(cStr *C.char) (string, bool) { 588 | if cStr == nil { 589 | return "", false 590 | } 591 | return C.GoString(cStr), true 592 | } 593 | -------------------------------------------------------------------------------- /avfilter/avfilter30.go: -------------------------------------------------------------------------------- 1 | // +build ffmpeg30 2 | 3 | package avfilter 4 | 5 | func (l *Link) Status() int { 6 | return int(l.CAVFilterLink.status) 7 | } 8 | 9 | func (l *Link) FrameCount() int64 { 10 | return int64(l.CAVFilterLink.frame_count) 11 | } 12 | -------------------------------------------------------------------------------- /avfilter/avfilter_test.go: -------------------------------------------------------------------------------- 1 | package avfilter 2 | 3 | import ( 4 | "math" 5 | "os" 6 | "strings" 7 | "testing" 8 | 9 | "github.com/imkira/go-libav/avutil" 10 | "github.com/shirou/gopsutil/process" 11 | ) 12 | 13 | func TestVersion(t *testing.T) { 14 | major, minor, micro := Version() 15 | if major < 6 || minor < 0 || micro < 0 { 16 | t.Fatalf("Invalid version") 17 | } 18 | } 19 | 20 | func TestNewFilterFromC(t *testing.T) { 21 | ctx := NewFilterFromC(nil) 22 | if ctx == nil { 23 | t.Fatalf("Expecting filter") 24 | } 25 | } 26 | 27 | func TestGraphRequestOldest(t *testing.T) { 28 | graph := testNewGraph(t) 29 | defer graph.Free() 30 | 31 | result := graph.RequestOldest() 32 | if result.(*avutil.Error).Code() != avutil.ErrorCodeEOF { 33 | t.Fatalf("[TestGraphRequestOldest] result = %d, NG, expected = %d", 34 | result.(*avutil.Error).Code(), avutil.ErrorCodeEOF) 35 | } 36 | } 37 | 38 | func TestFilterByNameOK(t *testing.T) { 39 | filter := testFilterByName(t, "displace") 40 | expectedDiscription := "Displace pixels." 41 | if filter.Description() != expectedDiscription { 42 | t.Errorf("[TestFilterByNameOK] description expected: %s, got: %s", expectedDiscription, filter.Description()) 43 | } 44 | if filter.Flags() != FlagSupportTimelineInternal { 45 | t.Errorf("[TestFilterByNameOK] flags expected: 0x%x, got: 0x%x", FlagSupportTimelineInternal, filter.Flags()) 46 | } 47 | } 48 | 49 | func TestFilterByNameRequiredNameParam(t *testing.T) { 50 | filter := FindFilterByName("") 51 | if filter != nil { 52 | t.Fatalf("Expecting filter is nil") 53 | } 54 | } 55 | 56 | func TestFilterByNameInvalidNameParam(t *testing.T) { 57 | filter := FindFilterByName("aaa") 58 | if filter != nil { 59 | t.Fatalf("Expecting filter is nil") 60 | } 61 | } 62 | 63 | func TestGraphAddFilterOK(t *testing.T) { 64 | graph := testNewGraph(t) 65 | defer graph.Free() 66 | testGraphAddFilter(t, graph, "scale", "rescale") 67 | testGraphAddFilter(t, graph, "fps", "fps1") 68 | if graph.NumberOfFilters() != 2 { 69 | t.Fatalf("[TestAddFilterOK] number of filters expected: 2, got: %d", graph.NumberOfFilters()) 70 | } 71 | } 72 | 73 | func TestGraphNewFreeLeak10M(t *testing.T) { 74 | before := testMemoryUsed(t) 75 | for i := 0; i < 10000000; i++ { 76 | graph := testNewGraph(t) 77 | graph.Free() 78 | } 79 | testMemoryLeak(t, before, 50*1024*1024) 80 | } 81 | 82 | func TestGraphAutoConvert(t *testing.T) { 83 | graph := testNewGraph(t) 84 | defer graph.Free() 85 | 86 | result := graph.AutoConvertFlags() 87 | if result != 0 { 88 | t.Fatalf("[TestGraphAutoConvert] result=%d, NG expected=%d", result, 0) 89 | } 90 | 91 | graph.SetAutoConvertFlags(GraphAutoConvertFlagNone) 92 | result = graph.AutoConvertFlags() 93 | if result != math.MaxUint32 { 94 | t.Fatalf("[TestGraphAutoConvert] result=%d, NG expected=%d", result, math.MaxUint32) 95 | } 96 | 97 | graph.SetAutoConvertFlags(GraphAutoConvertFlagAll) 98 | result = graph.AutoConvertFlags() 99 | if result != 0 { 100 | t.Fatalf("[TestGraphAutoConvert] result=%d, NG expected=%d", result, 0) 101 | } 102 | } 103 | 104 | func testFilterByName(t *testing.T, name string) *Filter { 105 | filter := FindFilterByName(name) 106 | if filter == nil { 107 | t.Fatalf("Expecting filter") 108 | } 109 | if !strings.EqualFold(name, filter.Name()) { 110 | t.Fatalf("[testFilterByName] name expected: %s, got: %s", name, filter.Name()) 111 | } 112 | return filter 113 | } 114 | 115 | func testGraphAddFilter(t *testing.T, graph *Graph, name, id string) *Context { 116 | filter := testFilterByName(t, name) 117 | ctx, err := graph.AddFilter(filter, id) 118 | if err != nil { 119 | t.Fatal(err) 120 | } 121 | if ctx == nil { 122 | t.Fatalf("Expecting filter context") 123 | } 124 | if !strings.EqualFold(id, ctx.Name()) { 125 | t.Fatalf("[testAddFilter] context name expected: %s, got: %s", id, ctx.Name()) 126 | } 127 | if ctx.Filter() == nil { 128 | t.Fatalf("Expecting filter") 129 | } 130 | if ctx.NumberOfInputs() <= 0 { 131 | t.Fatalf("[testAddFilter] number of inputs expected: greater than 0, got: %d", ctx.NumberOfInputs()) 132 | } 133 | if ctx.NumberOfOutputs() <= 0 { 134 | t.Fatalf("[testAddFilter] number of outputs expected: greater than 0, got: %d", ctx.NumberOfOutputs()) 135 | } 136 | return ctx 137 | } 138 | 139 | func testNewGraph(t *testing.T) *Graph { 140 | graph, err := NewGraph() 141 | if err != nil { 142 | t.Fatal(err) 143 | } 144 | if graph == nil { 145 | t.Fatalf("Expecting filter graph") 146 | } 147 | return graph 148 | } 149 | 150 | func TestInOutNewFreeLeak10M(t *testing.T) { 151 | before := testMemoryUsed(t) 152 | for i := 0; i < 10000000; i++ { 153 | io, err := NewInOut() 154 | if err != nil { 155 | t.Fatal(err) 156 | } 157 | io.Free() 158 | } 159 | testMemoryLeak(t, before, 50*1024*1024) 160 | } 161 | 162 | func testMemoryUsed(t *testing.T) uint64 { 163 | p, err := process.NewProcess(int32(os.Getpid())) 164 | if err != nil { 165 | t.Fatal(err) 166 | } 167 | info, err := p.MemoryInfo() 168 | if err != nil { 169 | t.Fatal(err) 170 | } 171 | return info.RSS 172 | } 173 | 174 | func testMemoryLeak(t *testing.T, before uint64, diff uint64) { 175 | after := testMemoryUsed(t) 176 | if after > before && after-before > diff { 177 | t.Fatalf("memory leak detected: %d bytes", after-before) 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /avformat/avformat.go: -------------------------------------------------------------------------------- 1 | package avformat 2 | 3 | //#include 4 | //#include 5 | //#include 6 | //#include 7 | // 8 | //#ifdef AVFMT_FLAG_FAST_SEEK 9 | //#define GO_AVFMT_FLAG_FAST_SEEK AVFMT_FLAG_FAST_SEEK 10 | //#else 11 | //#define GO_AVFMT_FLAG_FAST_SEEK 0 12 | //#endif 13 | // 14 | //static const AVStream *go_av_streams_get(const AVStream **streams, unsigned int n) 15 | //{ 16 | // return streams[n]; 17 | //} 18 | // 19 | //static AVDictionary **go_av_alloc_dicts(int length) 20 | //{ 21 | // size_t size = sizeof(AVDictionary*) * length; 22 | // return (AVDictionary**)av_malloc(size); 23 | //} 24 | // 25 | //static void go_av_dicts_set(AVDictionary** arr, unsigned int n, AVDictionary *val) 26 | //{ 27 | // arr[n] = val; 28 | //} 29 | // 30 | // size_t sizeOfAVFormatContextFilename = sizeof(((AVFormatContext *)NULL)->filename); 31 | // 32 | // int GO_AVFORMAT_VERSION_MAJOR = LIBAVFORMAT_VERSION_MAJOR; 33 | // int GO_AVFORMAT_VERSION_MINOR = LIBAVFORMAT_VERSION_MINOR; 34 | // int GO_AVFORMAT_VERSION_MICRO = LIBAVFORMAT_VERSION_MICRO; 35 | // 36 | //typedef int (*AVFormatContextIOOpenCallback)(struct AVFormatContext *s, AVIOContext **pb, const char *url, int flags, AVDictionary **options); 37 | //typedef void (*AVFormatContextIOCloseCallback)(struct AVFormatContext *s, AVIOContext *pb); 38 | // 39 | // #cgo pkg-config: libavformat libavutil 40 | import "C" 41 | 42 | import ( 43 | "errors" 44 | "strings" 45 | "time" 46 | "unsafe" 47 | 48 | "github.com/imkira/go-libav/avcodec" 49 | "github.com/imkira/go-libav/avutil" 50 | ) 51 | 52 | var ( 53 | ErrAllocationError = errors.New("allocation error") 54 | ErrInvalidArgumentSize = errors.New("invalid argument size") 55 | ) 56 | 57 | type Flags int 58 | 59 | const ( 60 | FlagNoFile Flags = C.AVFMT_NOFILE 61 | FlagNeedNumber Flags = C.AVFMT_NEEDNUMBER 62 | FlagShowIDs Flags = C.AVFMT_SHOW_IDS 63 | FlagRawPicture Flags = C.AVFMT_RAWPICTURE 64 | FlagGlobalHeader Flags = C.AVFMT_GLOBALHEADER 65 | FlagNoTimestamps Flags = C.AVFMT_NOTIMESTAMPS 66 | FlagGenericIndex Flags = C.AVFMT_GENERIC_INDEX 67 | FlagTSDiscont Flags = C.AVFMT_TS_DISCONT 68 | FlagVariableFPS Flags = C.AVFMT_VARIABLE_FPS 69 | FlagNoDimensions Flags = C.AVFMT_NODIMENSIONS 70 | FlagNoStreams Flags = C.AVFMT_NOSTREAMS 71 | FlagNoBinSearch Flags = C.AVFMT_NOBINSEARCH 72 | FlagNoGenSearch Flags = C.AVFMT_NOGENSEARCH 73 | FlagNoByteSeek Flags = C.AVFMT_NO_BYTE_SEEK 74 | FlagAllowFlush Flags = C.AVFMT_ALLOW_FLUSH 75 | FlagTSNonStrict Flags = C.AVFMT_TS_NONSTRICT 76 | FlagTSNegative Flags = C.AVFMT_TS_NEGATIVE 77 | FlagSeekToPTS Flags = C.AVFMT_SEEK_TO_PTS 78 | ) 79 | 80 | type ContextFlags int 81 | 82 | const ( 83 | ContextFlagGenPTS ContextFlags = C.AVFMT_FLAG_GENPTS 84 | ContextFlagIgnoreIndex ContextFlags = C.AVFMT_FLAG_IGNIDX 85 | ContextFlagNonBlock ContextFlags = C.AVFMT_FLAG_NONBLOCK 86 | ContextFlagIgnoreDTS ContextFlags = C.AVFMT_FLAG_IGNDTS 87 | ContextFlagNoFillin ContextFlags = C.AVFMT_FLAG_NOFILLIN 88 | ContextFlagNoParse ContextFlags = C.AVFMT_FLAG_NOPARSE 89 | ContextFlagNoBuffer ContextFlags = C.AVFMT_FLAG_NOBUFFER 90 | ContextFlagCustomIO ContextFlags = C.AVFMT_FLAG_CUSTOM_IO 91 | ContextFlagDiscardCorrupt ContextFlags = C.AVFMT_FLAG_DISCARD_CORRUPT 92 | ContextFlagFlushPackets ContextFlags = C.AVFMT_FLAG_FLUSH_PACKETS 93 | ContextFlagBitExact ContextFlags = C.AVFMT_FLAG_BITEXACT 94 | ContextFlagMP4ALATM ContextFlags = C.AVFMT_FLAG_MP4A_LATM 95 | ContextFlagSortDTS ContextFlags = C.AVFMT_FLAG_SORT_DTS 96 | ContextFlagPrivOpt ContextFlags = C.AVFMT_FLAG_PRIV_OPT 97 | ContextFlagKeepSideData ContextFlags = C.AVFMT_FLAG_KEEP_SIDE_DATA 98 | ContextFlagFastSeek ContextFlags = C.GO_AVFMT_FLAG_FAST_SEEK 99 | ) 100 | 101 | type ContextExtraFlags int 102 | 103 | const ( 104 | ContextExtraFlagNoHeader ContextExtraFlags = C.AVFMTCTX_NOHEADER 105 | ) 106 | 107 | type AvoidFlags int 108 | 109 | const ( 110 | AvoidFlagNegTSAuto AvoidFlags = C.AVFMT_AVOID_NEG_TS_AUTO 111 | AvoidFlagNegTSMakeNonNegative AvoidFlags = C.AVFMT_AVOID_NEG_TS_MAKE_NON_NEGATIVE 112 | AvoidFlagNegTSMakeZero AvoidFlags = C.AVFMT_AVOID_NEG_TS_MAKE_ZERO 113 | ) 114 | 115 | type Disposition int 116 | 117 | const ( 118 | DispositionDefault Disposition = C.AV_DISPOSITION_DEFAULT 119 | DispositionDub Disposition = C.AV_DISPOSITION_DUB 120 | DispositionOriginal Disposition = C.AV_DISPOSITION_ORIGINAL 121 | DispositionComment Disposition = C.AV_DISPOSITION_COMMENT 122 | DispositionLyrics Disposition = C.AV_DISPOSITION_LYRICS 123 | DispositionKaraoke Disposition = C.AV_DISPOSITION_KARAOKE 124 | DispositionForced Disposition = C.AV_DISPOSITION_FORCED 125 | DispositionHearingImpaired Disposition = C.AV_DISPOSITION_HEARING_IMPAIRED 126 | DispositionVisualImpaired Disposition = C.AV_DISPOSITION_VISUAL_IMPAIRED 127 | DispositionCleanEffects Disposition = C.AV_DISPOSITION_CLEAN_EFFECTS 128 | DispositionAttachedPic Disposition = C.AV_DISPOSITION_ATTACHED_PIC 129 | DispositionCaptions Disposition = C.AV_DISPOSITION_CAPTIONS 130 | DispositionDescriptions Disposition = C.AV_DISPOSITION_DESCRIPTIONS 131 | DispositionMetadata Disposition = C.AV_DISPOSITION_METADATA 132 | ) 133 | 134 | type EventFlags int 135 | 136 | const ( 137 | EventFlagMetadataUpdated EventFlags = C.AVFMT_EVENT_FLAG_METADATA_UPDATED 138 | ) 139 | 140 | type IOFlags int 141 | 142 | const ( 143 | IOFlagRead IOFlags = C.AVIO_FLAG_READ 144 | IOFlagWrite IOFlags = C.AVIO_FLAG_WRITE 145 | IOFlagReadWrite IOFlags = C.AVIO_FLAG_READ_WRITE 146 | IOFlagNonblock IOFlags = C.AVIO_FLAG_NONBLOCK 147 | IOFlagDirect IOFlags = C.AVIO_FLAG_DIRECT 148 | ) 149 | 150 | type SeekFlags int 151 | 152 | const ( 153 | SeekFlagNone SeekFlags = 0 154 | SeekFlagBackward SeekFlags = C.AVSEEK_FLAG_BACKWARD 155 | SeekFlagByte SeekFlags = C.AVSEEK_FLAG_BYTE 156 | SeekFlagAny SeekFlags = C.AVSEEK_FLAG_ANY 157 | SeekFlagFrame SeekFlags = C.AVSEEK_FLAG_FRAME 158 | ) 159 | 160 | func init() { 161 | RegisterAll() 162 | } 163 | 164 | func Version() (int, int, int) { 165 | return int(C.GO_AVFORMAT_VERSION_MAJOR), int(C.GO_AVFORMAT_VERSION_MINOR), int(C.GO_AVFORMAT_VERSION_MICRO) 166 | } 167 | 168 | func RegisterAll() { 169 | C.av_register_all() 170 | } 171 | 172 | type CodecTagList struct { 173 | CAVCodecTag **C.struct_AVCodecTag 174 | } 175 | 176 | func NewCodecTagListFromC(cCodecTag unsafe.Pointer) *CodecTagList { 177 | return &CodecTagList{CAVCodecTag: (**C.struct_AVCodecTag)(cCodecTag)} 178 | } 179 | 180 | func (ctm *CodecTagList) IDByTag(tag uint) avcodec.CodecID { 181 | return (avcodec.CodecID)(C.av_codec_get_id(ctm.CAVCodecTag, (C.uint)(tag))) 182 | } 183 | 184 | func (ctm *CodecTagList) TagByID(id avcodec.CodecID) uint { 185 | return (uint)(C.av_codec_get_tag(ctm.CAVCodecTag, (C.enum_AVCodecID)(id))) 186 | } 187 | 188 | type Input struct { 189 | CAVInputFormat *C.AVInputFormat 190 | } 191 | 192 | func FindInputByShortName(shortName string) *Input { 193 | cShortName := C.CString(shortName) 194 | defer C.free(unsafe.Pointer(cShortName)) 195 | cInput := C.av_find_input_format(cShortName) 196 | if cInput == nil { 197 | return nil 198 | } 199 | return NewInputFromC(unsafe.Pointer(cInput)) 200 | } 201 | 202 | func NewInputFromC(cInput unsafe.Pointer) *Input { 203 | return &Input{CAVInputFormat: (*C.AVInputFormat)(cInput)} 204 | } 205 | 206 | func (f *Input) PrivateClass() *avutil.Class { 207 | if f.CAVInputFormat.priv_class == nil { 208 | return nil 209 | } 210 | return avutil.NewClassFromC(unsafe.Pointer(f.CAVInputFormat.priv_class)) 211 | } 212 | 213 | func (f *Input) Names() []string { 214 | return cStringSplit(f.CAVInputFormat.name, ",") 215 | } 216 | 217 | func (f *Input) LongName() string { 218 | str, _ := f.LongNameOk() 219 | return str 220 | } 221 | 222 | func (f *Input) LongNameOk() (string, bool) { 223 | return cStringToStringOk(f.CAVInputFormat.long_name) 224 | } 225 | 226 | func (f *Input) MimeTypes() []string { 227 | return cStringSplit(f.CAVInputFormat.mime_type, ",") 228 | } 229 | 230 | func (f *Input) Extensions() []string { 231 | return cStringSplit(f.CAVInputFormat.extensions, ",") 232 | } 233 | 234 | func (f *Input) CodecTags() *CodecTagList { 235 | if f.CAVInputFormat.codec_tag == nil { 236 | return nil 237 | } 238 | return NewCodecTagListFromC(unsafe.Pointer(f.CAVInputFormat.codec_tag)) 239 | } 240 | 241 | func (f *Input) Flags() Flags { 242 | return Flags(f.CAVInputFormat.flags) 243 | } 244 | 245 | type ProbeData struct { 246 | CAVProbeData C.AVProbeData 247 | } 248 | 249 | func NewProbeData() *ProbeData { 250 | return &ProbeData{} 251 | } 252 | 253 | func (pd *ProbeData) Free() { 254 | defer C.free(unsafe.Pointer(pd.CAVProbeData.filename)) 255 | pd.CAVProbeData.filename = nil 256 | defer C.av_freep(unsafe.Pointer(&pd.CAVProbeData.buf)) 257 | pd.CAVProbeData.buf_size = 0 258 | defer C.free(unsafe.Pointer(pd.CAVProbeData.mime_type)) 259 | pd.CAVProbeData.mime_type = nil 260 | } 261 | 262 | func (pd *ProbeData) SetFileName(fileName *string) error { 263 | C.free(unsafe.Pointer(pd.CAVProbeData.filename)) 264 | if fileName == nil { 265 | pd.CAVProbeData.filename = nil 266 | return nil 267 | } 268 | pd.CAVProbeData.filename = C.CString(*fileName) 269 | if pd.CAVProbeData.filename == nil { 270 | return ErrAllocationError 271 | } 272 | return nil 273 | } 274 | 275 | func (pd *ProbeData) SetBuffer(buffer []byte) error { 276 | pd.CAVProbeData.buf_size = 0 277 | C.av_freep(unsafe.Pointer(&pd.CAVProbeData.buf)) 278 | size := C.size_t(len(buffer)) 279 | extraSize := C.size_t(C.AVPROBE_PADDING_SIZE) 280 | buf := C.av_malloc(size + extraSize) 281 | if buf == nil { 282 | return ErrAllocationError 283 | } 284 | if size != 0 { 285 | C.memcpy(buf, unsafe.Pointer(&buffer[0]), size) 286 | } 287 | C.memset(unsafe.Pointer(uintptr(buf)+uintptr(size)), 0, extraSize) 288 | pd.CAVProbeData.buf = (*C.uchar)(buf) 289 | pd.CAVProbeData.buf_size = C.int(size) 290 | return nil 291 | } 292 | 293 | func (pd *ProbeData) SetMimeType(mimeType *string) error { 294 | C.free(unsafe.Pointer(pd.CAVProbeData.mime_type)) 295 | if mimeType == nil { 296 | pd.CAVProbeData.mime_type = nil 297 | return nil 298 | } 299 | pd.CAVProbeData.mime_type = C.CString(*mimeType) 300 | if pd.CAVProbeData.mime_type == nil { 301 | return ErrAllocationError 302 | } 303 | return nil 304 | } 305 | 306 | func ProbeInput(pd *ProbeData, isOpened bool) *Input { 307 | cInput := C.av_probe_input_format(&pd.CAVProbeData, boolToCInt(isOpened)) 308 | if cInput == nil { 309 | return nil 310 | } 311 | return NewInputFromC(unsafe.Pointer(cInput)) 312 | } 313 | 314 | func ProbeInputWithScore(pd *ProbeData, isOpened bool, scoreMax int) (*Input, int) { 315 | cscoreMax := C.int(scoreMax) 316 | cInput := C.av_probe_input_format2(&pd.CAVProbeData, boolToCInt(isOpened), &cscoreMax) 317 | if cInput == nil { 318 | return nil, 0 319 | } 320 | return NewInputFromC(unsafe.Pointer(cInput)), int(cscoreMax) 321 | } 322 | 323 | type Output struct { 324 | CAVOutputFormat *C.AVOutputFormat 325 | } 326 | 327 | func NewOutputFromC(cOutput unsafe.Pointer) *Output { 328 | return &Output{CAVOutputFormat: (*C.AVOutputFormat)(cOutput)} 329 | } 330 | 331 | func (f *Output) QueryCodec(codecID avcodec.CodecID) (bool, bool) { 332 | return f.QueryCodecWithCompliance(codecID, avcodec.ComplianceNormal) 333 | } 334 | 335 | func (f *Output) QueryCodecWithCompliance(codecID avcodec.CodecID, compliance avcodec.Compliance) (bool, bool) { 336 | res := C.avformat_query_codec(f.CAVOutputFormat, (C.enum_AVCodecID)(codecID), (C.int)(compliance)) 337 | switch res { 338 | case 0: 339 | return false, true 340 | case 1: 341 | return true, true 342 | default: 343 | return false, false 344 | } 345 | } 346 | 347 | func (f *Output) PrivateClass() *avutil.Class { 348 | if f.CAVOutputFormat.priv_class == nil { 349 | return nil 350 | } 351 | return avutil.NewClassFromC(unsafe.Pointer(f.CAVOutputFormat.priv_class)) 352 | } 353 | 354 | func (f *Output) Name() string { 355 | str, _ := f.NameOk() 356 | return str 357 | } 358 | 359 | func (f *Output) NameOk() (string, bool) { 360 | return cStringToStringOk(f.CAVOutputFormat.name) 361 | } 362 | 363 | func (f *Output) LongName() string { 364 | str, _ := f.LongNameOk() 365 | return str 366 | } 367 | 368 | func (f *Output) LongNameOk() (string, bool) { 369 | return cStringToStringOk(f.CAVOutputFormat.long_name) 370 | } 371 | 372 | func (f *Output) MimeType() string { 373 | str, _ := f.MimeTypeOk() 374 | return str 375 | } 376 | 377 | func (f *Output) MimeTypeOk() (string, bool) { 378 | return cStringToStringOk(f.CAVOutputFormat.mime_type) 379 | } 380 | 381 | func (f *Output) Extensions() []string { 382 | return cStringSplit(f.CAVOutputFormat.extensions, ",") 383 | } 384 | 385 | func (f *Output) AudioCodecID() avcodec.CodecID { 386 | return (avcodec.CodecID)(f.CAVOutputFormat.audio_codec) 387 | } 388 | 389 | func (f *Output) VideoCodecID() avcodec.CodecID { 390 | return (avcodec.CodecID)(f.CAVOutputFormat.video_codec) 391 | } 392 | 393 | func (f *Output) SubtitleCodecID() avcodec.CodecID { 394 | return (avcodec.CodecID)(f.CAVOutputFormat.subtitle_codec) 395 | } 396 | 397 | func (f *Output) Flags() Flags { 398 | return Flags(f.CAVOutputFormat.flags) 399 | } 400 | 401 | func (f *Output) CodecTags() *CodecTagList { 402 | if f.CAVOutputFormat.codec_tag == nil { 403 | return nil 404 | } 405 | return NewCodecTagListFromC(unsafe.Pointer(f.CAVOutputFormat.codec_tag)) 406 | } 407 | 408 | func (f *Output) DataCodecID() avcodec.CodecID { 409 | return (avcodec.CodecID)(f.CAVOutputFormat.data_codec) 410 | } 411 | 412 | func (f *Output) GuessCodecID(filename string, mediaType avutil.MediaType) avcodec.CodecID { 413 | cFilename := C.CString(filename) 414 | defer C.free(unsafe.Pointer(cFilename)) 415 | return (avcodec.CodecID)(C.av_guess_codec(f.CAVOutputFormat, nil, cFilename, nil, C.enum_AVMediaType(mediaType))) 416 | } 417 | 418 | func GuessOutputFromShortName(shortName string) *Output { 419 | cShortName := C.CString(shortName) 420 | defer C.free(unsafe.Pointer(cShortName)) 421 | cOutput := C.av_guess_format(cShortName, nil, nil) 422 | if cOutput == nil { 423 | return nil 424 | } 425 | return NewOutputFromC(unsafe.Pointer(cOutput)) 426 | } 427 | 428 | func GuessOutputFromFileName(fileName string) *Output { 429 | cFileName := C.CString(fileName) 430 | defer C.free(unsafe.Pointer(cFileName)) 431 | cOutput := C.av_guess_format(nil, cFileName, nil) 432 | if cOutput == nil { 433 | return nil 434 | } 435 | return NewOutputFromC(unsafe.Pointer(cOutput)) 436 | } 437 | 438 | func GuessOutputFromMimeType(mimeType string) *Output { 439 | cMimeType := C.CString(mimeType) 440 | defer C.free(unsafe.Pointer(cMimeType)) 441 | cOutput := C.av_guess_format(nil, nil, cMimeType) 442 | if cOutput == nil { 443 | return nil 444 | } 445 | return NewOutputFromC(unsafe.Pointer(cOutput)) 446 | } 447 | 448 | type Stream struct { 449 | CAVStream *C.AVStream 450 | } 451 | 452 | func NewStreamFromC(cStream unsafe.Pointer) *Stream { 453 | return &Stream{CAVStream: (*C.AVStream)(cStream)} 454 | } 455 | 456 | func (s *Stream) Index() int { 457 | return int(s.CAVStream.index) 458 | } 459 | 460 | func (s *Stream) ID() int { 461 | return int(s.CAVStream.id) 462 | } 463 | 464 | func (s *Stream) CodecContext() *avcodec.Context { 465 | if s.CAVStream.codec == nil { 466 | return nil 467 | } 468 | return avcodec.NewContextFromC(unsafe.Pointer(s.CAVStream.codec)) 469 | } 470 | 471 | func (s *Stream) TimeBase() *avutil.Rational { 472 | tb := &s.CAVStream.time_base 473 | return avutil.NewRational(int(tb.num), int(tb.den)) 474 | } 475 | 476 | func (s *Stream) SetTimeBase(timeBase *avutil.Rational) { 477 | s.CAVStream.time_base.num = (C.int)(timeBase.Numerator()) 478 | s.CAVStream.time_base.den = (C.int)(timeBase.Denominator()) 479 | } 480 | 481 | func (s *Stream) SampleAspectRatio() *avutil.Rational { 482 | sar := &s.CAVStream.sample_aspect_ratio 483 | return avutil.NewRational(int(sar.num), int(sar.den)) 484 | } 485 | 486 | func (s *Stream) SetSampleAspectRatio(aspectRatio *avutil.Rational) { 487 | s.CAVStream.sample_aspect_ratio.num = (C.int)(aspectRatio.Numerator()) 488 | s.CAVStream.sample_aspect_ratio.den = (C.int)(aspectRatio.Denominator()) 489 | } 490 | 491 | func (s *Stream) StartTime() int64 { 492 | return int64(s.CAVStream.start_time) 493 | } 494 | 495 | func (s *Stream) RawDuration() int64 { 496 | return int64(s.CAVStream.duration) 497 | } 498 | 499 | func (s *Stream) Duration() time.Duration { 500 | timeBase := s.TimeBase().Float64() 501 | return time.Duration((timeBase * float64(s.RawDuration())) * 1000 * 1000 * 1000) 502 | } 503 | 504 | func (s *Stream) NumberOfFrames() int64 { 505 | return int64(s.CAVStream.nb_frames) 506 | } 507 | 508 | func (s *Stream) Disposition() Disposition { 509 | return Disposition(s.CAVStream.disposition) 510 | } 511 | 512 | func (s *Stream) SetDisposition(disposition Disposition) { 513 | s.CAVStream.disposition = C.int(disposition) 514 | } 515 | 516 | func (s *Stream) MetaData() *avutil.Dictionary { 517 | return avutil.NewDictionaryFromC(unsafe.Pointer(&s.CAVStream.metadata)) 518 | } 519 | 520 | func (s *Stream) SetMetaData(metaData *avutil.Dictionary) { 521 | var cMetaData *C.AVDictionary 522 | if metaData != nil { 523 | cMetaData = (*C.AVDictionary)(metaData.Value()) 524 | } 525 | s.CAVStream.metadata = cMetaData 526 | } 527 | 528 | func (s *Stream) AverageFrameRate() *avutil.Rational { 529 | return avutil.NewRationalFromC(unsafe.Pointer(&s.CAVStream.avg_frame_rate)) 530 | } 531 | 532 | func (s *Stream) SetAverageFrameRate(frameRate *avutil.Rational) { 533 | s.CAVStream.avg_frame_rate.num = (C.int)(frameRate.Numerator()) 534 | s.CAVStream.avg_frame_rate.den = (C.int)(frameRate.Denominator()) 535 | } 536 | 537 | func (s *Stream) RealFrameRate() *avutil.Rational { 538 | r := C.av_stream_get_r_frame_rate(s.CAVStream) 539 | return avutil.NewRationalFromC(unsafe.Pointer(&r)) 540 | } 541 | 542 | func (s *Stream) SetRealFrameRate(frameRate *avutil.Rational) { 543 | s.CAVStream.r_frame_rate.num = (C.int)(frameRate.Numerator()) 544 | s.CAVStream.r_frame_rate.den = (C.int)(frameRate.Denominator()) 545 | } 546 | 547 | func (s *Stream) SetFirstDTS(firstDTS int64) { 548 | s.CAVStream.first_dts = (C.int64_t)(firstDTS) 549 | } 550 | 551 | func (s *Stream) FirstDTS() int64 { 552 | return int64(s.CAVStream.first_dts) 553 | } 554 | 555 | func (s *Stream) EndPTS() int64 { 556 | return int64(C.av_stream_get_end_pts(s.CAVStream)) 557 | } 558 | 559 | type Context struct { 560 | CAVFormatContext *C.AVFormatContext 561 | } 562 | 563 | func NewContextForInput() (*Context, error) { 564 | cCtx := C.avformat_alloc_context() 565 | if cCtx == nil { 566 | return nil, ErrAllocationError 567 | } 568 | return NewContextFromC(unsafe.Pointer(cCtx)), nil 569 | } 570 | 571 | func NewContextForOutput(output *Output) (*Context, error) { 572 | var cCtx *C.AVFormatContext 573 | code := C.avformat_alloc_output_context2(&cCtx, output.CAVOutputFormat, nil, nil) 574 | if code < 0 { 575 | return nil, avutil.NewErrorFromCode(avutil.ErrorCode(code)) 576 | } 577 | return NewContextFromC(unsafe.Pointer(cCtx)), nil 578 | } 579 | 580 | func NewContextFromC(cCtx unsafe.Pointer) *Context { 581 | return &Context{CAVFormatContext: (*C.AVFormatContext)(cCtx)} 582 | } 583 | 584 | func (ctx *Context) Free() { 585 | if ctx.CAVFormatContext != nil { 586 | defer C.avformat_free_context(ctx.CAVFormatContext) 587 | ctx.CAVFormatContext = nil 588 | } 589 | } 590 | 591 | func (ctx *Context) Class() *avutil.Class { 592 | if ctx.CAVFormatContext.av_class == nil { 593 | return nil 594 | } 595 | return avutil.NewClassFromC(unsafe.Pointer(ctx.CAVFormatContext.av_class)) 596 | } 597 | 598 | func (ctx *Context) Input() *Input { 599 | if ctx.CAVFormatContext.iformat == nil { 600 | return nil 601 | } 602 | return NewInputFromC(unsafe.Pointer(ctx.CAVFormatContext.iformat)) 603 | } 604 | 605 | func (ctx *Context) Output() *Output { 606 | if ctx.CAVFormatContext.oformat == nil { 607 | return nil 608 | } 609 | return NewOutputFromC(unsafe.Pointer(ctx.CAVFormatContext.oformat)) 610 | } 611 | 612 | func (ctx *Context) IOContext() *IOContext { 613 | if ctx.CAVFormatContext.pb == nil { 614 | return nil 615 | } 616 | return NewIOContextFromC(unsafe.Pointer(ctx.CAVFormatContext.pb)) 617 | } 618 | 619 | func (ctx *Context) SetIOContext(ioCtx *IOContext) { 620 | var cIOCtx *C.AVIOContext 621 | if ioCtx != nil { 622 | cIOCtx = ioCtx.CAVIOContext 623 | } 624 | ctx.CAVFormatContext.pb = cIOCtx 625 | } 626 | 627 | func (ctx *Context) NewStream() (*Stream, error) { 628 | return ctx.NewStreamWithCodec(nil) 629 | } 630 | 631 | func (ctx *Context) NewStreamWithCodec(codec *avcodec.Codec) (*Stream, error) { 632 | var cCodec *C.AVCodec 633 | if codec != nil { 634 | cCodec = (*C.AVCodec)(unsafe.Pointer(codec.CAVCodec)) 635 | } 636 | cStream := C.avformat_new_stream(ctx.CAVFormatContext, cCodec) 637 | if cStream == nil { 638 | return nil, ErrAllocationError 639 | } 640 | return NewStreamFromC(unsafe.Pointer(cStream)), nil 641 | } 642 | 643 | func (ctx *Context) NumberOfStreams() uint { 644 | return uint(ctx.CAVFormatContext.nb_streams) 645 | } 646 | 647 | func (ctx *Context) WriteHeader(options *avutil.Dictionary) error { 648 | var cOptions **C.AVDictionary 649 | if options != nil { 650 | cOptions = (**C.AVDictionary)(options.Pointer()) 651 | } 652 | code := C.avformat_write_header(ctx.CAVFormatContext, cOptions) 653 | if code < 0 { 654 | return avutil.NewErrorFromCode(avutil.ErrorCode(code)) 655 | } 656 | return nil 657 | } 658 | 659 | func (ctx *Context) WriteTrailer() error { 660 | code := C.av_write_trailer(ctx.CAVFormatContext) 661 | if code < 0 { 662 | return avutil.NewErrorFromCode(avutil.ErrorCode(code)) 663 | } 664 | return nil 665 | } 666 | 667 | func (ctx *Context) ReadFrame(pkt *avcodec.Packet) (bool, error) { 668 | cPkt := (*C.AVPacket)(unsafe.Pointer(pkt.CAVPacket)) 669 | code := C.av_read_frame(ctx.CAVFormatContext, cPkt) 670 | if code < 0 { 671 | if avutil.ErrorCode(code) == avutil.ErrorCodeEOF { 672 | return false, nil 673 | } 674 | return false, avutil.NewErrorFromCode(avutil.ErrorCode(code)) 675 | } 676 | return true, nil 677 | } 678 | 679 | func (ctx *Context) WriteFrame(pkt *avcodec.Packet) error { 680 | var cPkt *C.AVPacket 681 | if cPkt != nil { 682 | cPkt = (*C.AVPacket)(unsafe.Pointer(pkt.CAVPacket)) 683 | } 684 | code := C.av_write_frame(ctx.CAVFormatContext, cPkt) 685 | if code < 0 { 686 | return avutil.NewErrorFromCode(avutil.ErrorCode(code)) 687 | } 688 | return nil 689 | } 690 | 691 | func (ctx *Context) InterleavedWriteFrame(pkt *avcodec.Packet) error { 692 | var cPkt *C.AVPacket 693 | if pkt != nil { 694 | cPkt = (*C.AVPacket)(unsafe.Pointer(pkt.CAVPacket)) 695 | } 696 | code := C.av_interleaved_write_frame(ctx.CAVFormatContext, cPkt) 697 | if code < 0 { 698 | return avutil.NewErrorFromCode(avutil.ErrorCode(code)) 699 | } 700 | return nil 701 | } 702 | 703 | func (ctx *Context) Streams() []*Stream { 704 | count := ctx.NumberOfStreams() 705 | if count <= 0 { 706 | return nil 707 | } 708 | streams := make([]*Stream, 0, count) 709 | for i := uint(0); i < count; i++ { 710 | cStream := C.go_av_streams_get(ctx.CAVFormatContext.streams, C.uint(i)) 711 | stream := NewStreamFromC(unsafe.Pointer(cStream)) 712 | streams = append(streams, stream) 713 | } 714 | return streams 715 | } 716 | 717 | func (ctx *Context) FileName() string { 718 | return C.GoString(&ctx.CAVFormatContext.filename[0]) 719 | } 720 | 721 | func (ctx *Context) SetFileName(fileName string) { 722 | cFileName := C.CString(fileName) 723 | defer C.free(unsafe.Pointer(cFileName)) 724 | C.av_strlcpy(&ctx.CAVFormatContext.filename[0], cFileName, C.sizeOfAVFormatContextFilename) 725 | } 726 | 727 | func (ctx *Context) StartTime() int64 { 728 | return int64(ctx.CAVFormatContext.start_time) 729 | } 730 | 731 | func (ctx *Context) Duration() int64 { 732 | return int64(ctx.CAVFormatContext.duration) 733 | } 734 | 735 | func (ctx *Context) SetDuration(duration int64) { 736 | ctx.CAVFormatContext.duration = (C.int64_t)(duration) 737 | } 738 | 739 | func (ctx *Context) BitRate() int64 { 740 | return int64(ctx.CAVFormatContext.bit_rate) 741 | } 742 | 743 | func (ctx *Context) MaxDelay() int { 744 | return int(ctx.CAVFormatContext.max_delay) 745 | } 746 | 747 | func (ctx *Context) SetMaxDelay(maxDelay int) { 748 | ctx.CAVFormatContext.max_delay = (C.int)(maxDelay) 749 | } 750 | 751 | func (ctx *Context) Flags() ContextFlags { 752 | return ContextFlags(ctx.CAVFormatContext.flags) 753 | } 754 | 755 | func (ctx *Context) SetFlags(flags ContextFlags) { 756 | ctx.CAVFormatContext.flags = (C.int)(flags) 757 | } 758 | 759 | func (ctx *Context) ExtraFlags() ContextExtraFlags { 760 | return ContextExtraFlags(ctx.CAVFormatContext.ctx_flags) 761 | } 762 | 763 | func (ctx *Context) AudioCodecID() avcodec.CodecID { 764 | return (avcodec.CodecID)(ctx.CAVFormatContext.audio_codec_id) 765 | } 766 | 767 | func (ctx *Context) VideoCodecID() avcodec.CodecID { 768 | return (avcodec.CodecID)(ctx.CAVFormatContext.video_codec_id) 769 | } 770 | 771 | func (ctx *Context) SubtitleCodecID() avcodec.CodecID { 772 | return (avcodec.CodecID)(ctx.CAVFormatContext.subtitle_codec_id) 773 | } 774 | 775 | func (ctx *Context) MetaData() *avutil.Dictionary { 776 | return avutil.NewDictionaryFromC(unsafe.Pointer(&ctx.CAVFormatContext.metadata)) 777 | } 778 | 779 | func (ctx *Context) SetMetaData(metaData *avutil.Dictionary) { 780 | var cMetaData *C.AVDictionary 781 | if metaData != nil { 782 | cMetaData = (*C.AVDictionary)(metaData.Value()) 783 | } 784 | ctx.CAVFormatContext.metadata = cMetaData 785 | } 786 | 787 | func (ctx *Context) DataCodecID() avcodec.CodecID { 788 | return (avcodec.CodecID)(ctx.CAVFormatContext.data_codec_id) 789 | } 790 | 791 | func (ctx *Context) IOOpenCallback() unsafe.Pointer { 792 | return unsafe.Pointer(ctx.CAVFormatContext.io_open) 793 | } 794 | 795 | func (ctx *Context) SetIOOpenCallback(callback unsafe.Pointer) { 796 | ctx.CAVFormatContext.io_open = (C.AVFormatContextIOOpenCallback)(callback) 797 | } 798 | 799 | func (ctx *Context) IOCloseCallback() unsafe.Pointer { 800 | return unsafe.Pointer(ctx.CAVFormatContext.io_close) 801 | } 802 | 803 | func (ctx *Context) SetIOCloseCallback(callback unsafe.Pointer) { 804 | ctx.CAVFormatContext.io_close = (C.AVFormatContextIOCloseCallback)(callback) 805 | } 806 | 807 | func (ctx *Context) OpenInput(fileName string, input *Input, options *avutil.Dictionary) error { 808 | cFileName := C.CString(fileName) 809 | defer C.free(unsafe.Pointer(cFileName)) 810 | var cInput *C.AVInputFormat 811 | if input != nil { 812 | cInput = input.CAVInputFormat 813 | } 814 | var cOptions **C.AVDictionary 815 | if options != nil { 816 | cOptions = (**C.AVDictionary)(options.Pointer()) 817 | } 818 | code := C.avformat_open_input(&ctx.CAVFormatContext, cFileName, cInput, cOptions) 819 | if code < 0 { 820 | return avutil.NewErrorFromCode(avutil.ErrorCode(code)) 821 | } 822 | return nil 823 | } 824 | 825 | func (ctx *Context) CloseInput() { 826 | C.avformat_close_input(&ctx.CAVFormatContext) 827 | } 828 | 829 | func (ctx *Context) FindStreamInfo(options []*avutil.Dictionary) error { 830 | var cOptions **C.AVDictionary 831 | count := ctx.NumberOfStreams() 832 | if count > 0 && options != nil { 833 | if uint(len(options)) < count { 834 | return ErrInvalidArgumentSize 835 | } 836 | cOptions = newCAVDictionaryArrayFromDictionarySlice(options[:count]) 837 | defer freeCAVDictionaryArray(cOptions) 838 | } 839 | code := C.avformat_find_stream_info(ctx.CAVFormatContext, cOptions) 840 | if code < 0 { 841 | return avutil.NewErrorFromCode(avutil.ErrorCode(code)) 842 | } 843 | return nil 844 | } 845 | 846 | func (ctx *Context) Dump(streamIndex int, url string, isOutput bool) { 847 | var cIsOutput C.int 848 | if isOutput { 849 | cIsOutput = C.int(1) 850 | } 851 | cURL := C.CString(url) 852 | defer C.free(unsafe.Pointer(cURL)) 853 | C.av_dump_format(ctx.CAVFormatContext, C.int(streamIndex), cURL, cIsOutput) 854 | } 855 | 856 | func (ctx *Context) GuessFrameRate(stream *Stream, frame *avutil.Frame) *avutil.Rational { 857 | cStream := (*C.AVStream)(unsafe.Pointer(stream.CAVStream)) 858 | var cFrame *C.AVFrame 859 | if frame != nil { 860 | cFrame = (*C.AVFrame)(unsafe.Pointer(frame.CAVFrame)) 861 | } 862 | r := C.av_guess_frame_rate(ctx.CAVFormatContext, cStream, cFrame) 863 | return avutil.NewRationalFromC(unsafe.Pointer(&r)) 864 | } 865 | 866 | func (ctx *Context) SeekToTimestamp(streamIndex int, min, target, max int64, flags SeekFlags) error { 867 | code := C.avformat_seek_file(ctx.CAVFormatContext, C.int(streamIndex), C.int64_t(min), C.int64_t(target), C.int64_t(max), C.int(flags)) 868 | if code < 0 { 869 | return avutil.NewErrorFromCode(avutil.ErrorCode(code)) 870 | } 871 | return nil 872 | } 873 | 874 | type IOContext struct { 875 | CAVIOContext *C.AVIOContext 876 | } 877 | 878 | func OpenIOContext(url string, flags IOFlags, cb *IOInterruptCallback, options *avutil.Dictionary) (*IOContext, error) { 879 | cURL := C.CString(url) 880 | defer C.free(unsafe.Pointer(cURL)) 881 | var cCb *C.AVIOInterruptCB 882 | if cb != nil { 883 | cCb = cb.CAVIOInterruptCB 884 | } 885 | var cOptions **C.AVDictionary 886 | if options != nil { 887 | cOptions = (**C.AVDictionary)(options.Pointer()) 888 | } 889 | var cCtx *C.AVIOContext 890 | code := C.avio_open2(&cCtx, cURL, (C.int)(flags), cCb, cOptions) 891 | if code < 0 { 892 | return nil, avutil.NewErrorFromCode(avutil.ErrorCode(code)) 893 | } 894 | return NewIOContextFromC(unsafe.Pointer(cCtx)), nil 895 | } 896 | 897 | func NewIOContextFromC(cCtx unsafe.Pointer) *IOContext { 898 | return &IOContext{CAVIOContext: (*C.AVIOContext)(cCtx)} 899 | } 900 | 901 | func (ctx *IOContext) Size() int64 { 902 | return int64(C.avio_size(ctx.CAVIOContext)) 903 | } 904 | 905 | func (ctx *IOContext) Close() error { 906 | if ctx.CAVIOContext != nil { 907 | code := C.avio_closep(&ctx.CAVIOContext) 908 | if code < 0 { 909 | return avutil.NewErrorFromCode(avutil.ErrorCode(code)) 910 | } 911 | } 912 | return nil 913 | } 914 | 915 | type IOInterruptCallback struct { 916 | CAVIOInterruptCB *C.AVIOInterruptCB 917 | } 918 | 919 | func NewIOInterruptCallbackFromC(cb unsafe.Pointer) *IOInterruptCallback { 920 | return &IOInterruptCallback{CAVIOInterruptCB: (*C.AVIOInterruptCB)(cb)} 921 | } 922 | 923 | func boolToCInt(b bool) C.int { 924 | if b { 925 | return 1 926 | } 927 | return 0 928 | } 929 | 930 | func cStringSplit(cStr *C.char, sep string) []string { 931 | str, ok := cStringToStringOk(cStr) 932 | if !ok { 933 | return nil 934 | } 935 | return strings.Split(str, sep) 936 | } 937 | 938 | func cStringToStringOk(cStr *C.char) (string, bool) { 939 | if cStr == nil { 940 | return "", false 941 | } 942 | return C.GoString(cStr), true 943 | } 944 | 945 | func newCAVDictionaryArrayFromDictionarySlice(dicts []*avutil.Dictionary) **C.AVDictionary { 946 | arr := C.go_av_alloc_dicts(C.int(len(dicts))) 947 | for i := range dicts { 948 | C.go_av_dicts_set(arr, C.uint(i), (*C.AVDictionary)(dicts[i].Value())) 949 | } 950 | return nil 951 | } 952 | 953 | func freeCAVDictionaryArray(arr **C.AVDictionary) { 954 | C.av_free(unsafe.Pointer(arr)) 955 | } 956 | 957 | func NumberedSequenceFormat(format string) bool { 958 | cFormat := C.CString(format) 959 | defer C.free(unsafe.Pointer(cFormat)) 960 | valid := C.av_filename_number_test(cFormat) 961 | if valid == 1 { 962 | return true 963 | } 964 | return false 965 | } 966 | 967 | func FormatNumberedSequence(format string, num int) (string, error) { 968 | cFormat := C.CString(format) 969 | defer C.free(unsafe.Pointer(cFormat)) 970 | size := C.size_t(1024) 971 | buf := (*C.char)(C.av_mallocz(size)) 972 | defer C.av_free(unsafe.Pointer(buf)) 973 | code := C.av_get_frame_filename(buf, C.int(size-1), cFormat, C.int(num)) 974 | if code < 0 { 975 | return "", avutil.NewErrorFromCode(avutil.ErrorCode(code)) 976 | } 977 | return C.GoString(buf), nil 978 | } 979 | -------------------------------------------------------------------------------- /avformat/avformat30.go: -------------------------------------------------------------------------------- 1 | // +build ffmpeg30 2 | 3 | package avformat 4 | 5 | //#include 6 | //#include 7 | //#include 8 | //#include 9 | // 10 | // #cgo pkg-config: libavformat libavutil 11 | import "C" 12 | 13 | import ( 14 | "unsafe" 15 | 16 | "github.com/imkira/go-libav/avcodec" 17 | "github.com/imkira/go-libav/avutil" 18 | ) 19 | 20 | func ApplyBitstreamFilters(codecCtx *avcodec.Context, pkt *avcodec.Packet, filtersCtx *avcodec.BitStreamFilterContext) error { 21 | cCodecCtx := (*C.AVCodecContext)(unsafe.Pointer(codecCtx.CAVCodecContext)) 22 | cPkt := (*C.AVPacket)(unsafe.Pointer(pkt.CAVPacket)) 23 | cFilters := (*C.AVBitStreamFilterContext)(unsafe.Pointer(filtersCtx.CAVBitStreamFilterContext)) 24 | code := C.av_apply_bitstream_filters(cCodecCtx, cPkt, cFilters) 25 | if code < 0 { 26 | return avutil.NewErrorFromCode(avutil.ErrorCode(code)) 27 | } 28 | return nil 29 | } 30 | -------------------------------------------------------------------------------- /avformat/avformat_test.go: -------------------------------------------------------------------------------- 1 | package avformat 2 | 3 | import ( 4 | "bytes" 5 | "crypto/rand" 6 | "fmt" 7 | "io/ioutil" 8 | "log" 9 | "math" 10 | "os" 11 | "path/filepath" 12 | "reflect" 13 | "testing" 14 | "unsafe" 15 | 16 | "github.com/imkira/go-libav/avcodec" 17 | "github.com/imkira/go-libav/avutil" 18 | "github.com/shirou/gopsutil/process" 19 | ) 20 | 21 | func TestVersion(t *testing.T) { 22 | major, minor, micro := Version() 23 | if major < 57 || minor < 0 || micro < 0 { 24 | t.Fatalf("Invalid version") 25 | } 26 | } 27 | 28 | func testInputFormatMatroska(t *testing.T, f *Input) { 29 | if f == nil { 30 | t.Fatalf("Expecting format") 31 | } 32 | names := f.Names() 33 | if !reflect.DeepEqual(names, []string{"matroska", "webm"}) { 34 | t.Fatalf("Expecting names but got %v", names) 35 | } 36 | longName, ok := f.LongNameOk() 37 | if !ok || longName != "Matroska / WebM" { 38 | t.Fatalf("Expecting name but got %s", longName) 39 | } 40 | mimeTypes := f.MimeTypes() 41 | if !reflect.DeepEqual(mimeTypes, []string{"audio/webm", "audio/x-matroska", "video/webm", "video/x-matroska"}) { 42 | t.Fatalf("Expecting mimeTypes but got %v", mimeTypes) 43 | } 44 | extensions := f.Extensions() 45 | if !reflect.DeepEqual(extensions, []string{"mkv", "mk3d", "mka", "mks"}) { 46 | t.Fatalf("Expecting extensions but got %v", extensions) 47 | } 48 | } 49 | 50 | func TestFindInputByShortName(t *testing.T) { 51 | shortNames := []string{ 52 | "matroska", 53 | } 54 | for _, shortName := range shortNames { 55 | f := FindInputByShortName(shortName) 56 | testInputFormatMatroska(t, f) 57 | } 58 | if FindInputByShortName("maaaaatroska") != nil { 59 | t.Fatalf("Not expecting format") 60 | } 61 | } 62 | 63 | func TestInputFlags(t *testing.T) { 64 | ctx, _ := NewContextForInput() 65 | defer ctx.Free() 66 | fixture := fixturePath("sample_mpeg4.mp4") 67 | err := ctx.OpenInput(fixture, nil, nil) 68 | if err != nil { 69 | t.Fatal(err) 70 | } 71 | result := ctx.Input().Flags() 72 | if result != FlagNoByteSeek { 73 | t.Fatalf("[TestFlags] result = %v, NG, expected = %v", result, FlagNoByteSeek) 74 | } 75 | } 76 | 77 | func TestProbeDataSetBuffer(t *testing.T) { 78 | pd := NewProbeData() 79 | defer pd.Free() 80 | maxSize := 1024 * 1024 * 16 81 | for size := 0; size < maxSize; size = int(math.Max(1, float64(size)*2)) { 82 | b := make([]byte, size) 83 | _, err := rand.Read(b) 84 | if err != nil { 85 | t.Fatal(err) 86 | } 87 | if err := pd.SetBuffer(b); err != nil { 88 | t.Fatal(err) 89 | } 90 | if pd.CAVProbeData.buf == nil { 91 | t.Fatalf("Expecting buf") 92 | } 93 | if got := int(pd.CAVProbeData.buf_size); got != size { 94 | t.Fatalf("Expecting size=%d got %d", size, got) 95 | } 96 | for i := 0; i < size; i++ { 97 | ptr := unsafe.Pointer(uintptr(unsafe.Pointer(pd.CAVProbeData.buf)) + uintptr(i)) 98 | c := *(*byte)(ptr) 99 | if c != b[i] { 100 | t.Fatalf("Invalid byte at offset=%d size=%d", i, size) 101 | } 102 | } 103 | probePaddingSize := 32 104 | for i := size; i < size+probePaddingSize; i++ { 105 | ptr := unsafe.Pointer(uintptr(unsafe.Pointer(pd.CAVProbeData.buf)) + uintptr(i)) 106 | c := *(*byte)(ptr) 107 | if c != 0 { 108 | t.Fatalf("Invalid byte at offset=%d size=%d", i, size) 109 | } 110 | } 111 | } 112 | } 113 | 114 | func TestProbeInput(t *testing.T) { 115 | pd := NewProbeData() 116 | defer pd.Free() 117 | if ProbeInput(pd, true) != nil { 118 | t.Fatalf("Not expecting format") 119 | } 120 | 121 | pd = NewProbeData() 122 | defer pd.Free() 123 | pd.SetFileName(avutil.String("file.mkv")) 124 | testInputFormatMatroska(t, ProbeInput(pd, true)) 125 | 126 | pd = NewProbeData() 127 | defer pd.Free() 128 | pd.SetMimeType(avutil.String("video/x-matroska")) 129 | testInputFormatMatroska(t, ProbeInput(pd, true)) 130 | 131 | pd = NewProbeData() 132 | defer pd.Free() 133 | matroskaHeader := []byte{ 134 | 0x1a, 0x45, 0xdf, 0xa3, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x42, 0x82, 0x89, 0x6d, 135 | 0x61, 0x74, 0x72, 0x6f, 0x73, 0x6b, 0x61, 0x00, 0x42, 0x87, 0x81, 0x02, 0x42, 0x85, 0x81, 0x02, 136 | } 137 | pd.SetBuffer(matroskaHeader) 138 | testInputFormatMatroska(t, ProbeInput(pd, true)) 139 | 140 | pd = NewProbeData() 141 | defer pd.Free() 142 | pd.SetBuffer(matroskaHeader) 143 | f, score := ProbeInputWithScore(pd, true, 0) 144 | testInputFormatMatroska(t, f) 145 | if score != 100 { 146 | t.Fatalf("Expecting score but got %d", score) 147 | } 148 | 149 | pd = NewProbeData() 150 | defer pd.Free() 151 | pd.SetBuffer(nil) 152 | if ProbeInput(pd, true) != nil { 153 | t.Fatalf("Not expecting format") 154 | } 155 | } 156 | 157 | func testOutputFormatMatroska(t *testing.T, f *Output) { 158 | if f == nil { 159 | t.Fatalf("Expecting format") 160 | } 161 | name, ok := f.NameOk() 162 | if !ok || name != "matroska" { 163 | t.Fatalf("Expecting name but got %s", name) 164 | } 165 | longName, ok := f.LongNameOk() 166 | if !ok || longName != "Matroska" { 167 | t.Fatalf("Expecting name but got %s", longName) 168 | } 169 | mimeType, ok := f.MimeTypeOk() 170 | if !ok || mimeType != "video/x-matroska" { 171 | t.Fatalf("Expecting mimetype but got %s", mimeType) 172 | } 173 | extensions := f.Extensions() 174 | if !reflect.DeepEqual(extensions, []string{"mkv"}) { 175 | t.Fatalf("Expecting extensions but got %v", extensions) 176 | } 177 | } 178 | 179 | func TestOutput_GuessCodecID(t *testing.T) { 180 | type testData struct { 181 | filename string 182 | expectedVideo string 183 | expectedAudio string 184 | } 185 | datas := []*testData{ 186 | { 187 | filename: "test.mp4", 188 | expectedVideo: "libx264", 189 | expectedAudio: "aac", 190 | }, 191 | { 192 | filename: "test.png", 193 | expectedVideo: "png", 194 | expectedAudio: "none", 195 | }, 196 | } 197 | 198 | for i, data := range datas { 199 | fmt := GuessOutputFromFileName(data.filename) 200 | codecID := fmt.GuessCodecID(data.filename, avutil.MediaTypeVideo) 201 | if codecID == avcodec.CodecIDNone { 202 | if data.expectedVideo != "none" { 203 | t.Fatalf("[case %d] expected %v, got any(ID=%v)", i+1, data.expectedVideo, codecID) 204 | } 205 | } else { 206 | codec := avcodec.FindEncoderByID(codecID) 207 | if codec.Name() != data.expectedVideo { 208 | t.Fatalf("[case %d] expected %v, got %v", i+1, data.expectedVideo, codec.Name()) 209 | } 210 | } 211 | 212 | codecID = fmt.GuessCodecID(data.filename, avutil.MediaTypeAudio) 213 | if codecID == avcodec.CodecIDNone { 214 | if data.expectedAudio != "none" { 215 | t.Fatalf("[case %d] expected %v, got any(ID=%v)", i+1, data.expectedAudio, codecID) 216 | } 217 | } else { 218 | codec := avcodec.FindEncoderByID(codecID) 219 | if codec.Name() != data.expectedAudio { 220 | t.Fatalf("[case %d] expected %v, got %v", i+1, data.expectedAudio, codec.Name()) 221 | } 222 | } 223 | } 224 | } 225 | 226 | func TestGuessOutputFromShortName(t *testing.T) { 227 | shortNames := []string{ 228 | "matroska", 229 | "MATROSKA", 230 | } 231 | for _, shortName := range shortNames { 232 | f := GuessOutputFromShortName(shortName) 233 | testOutputFormatMatroska(t, f) 234 | } 235 | if GuessOutputFromShortName("maaaaatroska") != nil { 236 | t.Fatalf("Not expecting format") 237 | } 238 | } 239 | 240 | func TestGuessOutputFromFileName(t *testing.T) { 241 | fileNames := []string{ 242 | "test.mkv", 243 | "test.MKV", 244 | "file://test.mkv", 245 | "http://example.com/test.mkv", 246 | } 247 | for _, fileName := range fileNames { 248 | f := GuessOutputFromFileName(fileName) 249 | testOutputFormatMatroska(t, f) 250 | } 251 | if GuessOutputFromFileName("maaaaatroska") != nil { 252 | t.Fatalf("Not expecting format") 253 | } 254 | } 255 | 256 | func TestGuessOutputFromMimeType(t *testing.T) { 257 | mimeTypes := []string{ 258 | "video/x-matroska", 259 | } 260 | for _, mimeType := range mimeTypes { 261 | f := GuessOutputFromMimeType(mimeType) 262 | if f == nil { 263 | t.Fatalf("Expecting format") 264 | } 265 | testOutputFormatMatroska(t, f) 266 | } 267 | if GuessOutputFromMimeType("video/x-maaaaatroska") != nil { 268 | t.Fatalf("Not expecting format") 269 | } 270 | } 271 | 272 | func TestNewContextForInput(t *testing.T) { 273 | ctx, err := NewContextForInput() 274 | if err != nil || ctx == nil { 275 | t.Fatalf("Expecting context") 276 | } 277 | defer ctx.Free() 278 | } 279 | 280 | func TestNewContextForOutput(t *testing.T) { 281 | output := GuessOutputFromShortName("matroska") 282 | if output == nil { 283 | t.Fatalf("Expecting output") 284 | } 285 | ctx, err := NewContextForOutput(output) 286 | if err != nil || ctx == nil { 287 | t.Fatalf("Expecting context") 288 | } 289 | defer ctx.Free() 290 | } 291 | 292 | func TestContextOpenInputNonExistent(t *testing.T) { 293 | ctx, _ := NewContextForInput() 294 | defer ctx.Free() 295 | err := ctx.OpenInput("foobarnonexistent", nil, nil).(*avutil.Error) 296 | if err == nil { 297 | defer ctx.CloseInput() 298 | t.Fatal(err) 299 | } 300 | if err.Error() != "No such file or directory" { 301 | t.Fatal(err) 302 | } 303 | } 304 | 305 | func TestContextOpenInputExistent(t *testing.T) { 306 | ctx, _ := NewContextForInput() 307 | defer ctx.Free() 308 | fixture := fixturePath("sample_mpeg4.mp4") 309 | err := ctx.OpenInput(fixture, nil, nil) 310 | if err != nil { 311 | t.Fatal(err) 312 | } 313 | defer ctx.CloseInput() 314 | } 315 | 316 | func TestContextOpenInputWithOptions(t *testing.T) { 317 | ctx, _ := NewContextForInput() 318 | defer ctx.Free() 319 | fixture := fixturePath("sample_mpeg4.mp4") 320 | 321 | options := avutil.NewDictionary() 322 | defer options.Free() 323 | options.Set("foo", "1") 324 | options.Set("export_all", "1") 325 | options.Set("bar", "1") 326 | 327 | err := ctx.OpenInput(fixture, nil, options) 328 | if err != nil { 329 | t.Fatal(err) 330 | } 331 | defer ctx.CloseInput() 332 | 333 | // consumed options disappear from the dictionary 334 | m := options.Map() 335 | if !reflect.DeepEqual(m, map[string]string{"foo": "1", "bar": "1"}) { 336 | t.Fatalf("Expecting map but got %v", m) 337 | } 338 | } 339 | 340 | func fixturePath(elem ...string) string { 341 | dir, err := os.Getwd() 342 | if err != nil { 343 | panic(err) 344 | } 345 | dir = filepath.Join(filepath.Dir(dir), "fixtures") 346 | path, err := filepath.Abs(filepath.Join(dir, filepath.Join(elem...))) 347 | if err != nil { 348 | panic(err) 349 | } 350 | return path 351 | } 352 | 353 | func TestSetFileName(t *testing.T) { 354 | ctx, _ := NewContextForInput() 355 | defer ctx.Free() 356 | 357 | var buff bytes.Buffer 358 | for i := 0; i < 1023; i++ { 359 | buff.WriteRune('a') 360 | ctx.SetFileName(buff.String()) 361 | result := ctx.FileName() 362 | if result != buff.String() { 363 | t.Fatalf("[TestSetFileName] result = %s, NG, expected = %s", result, buff.String()) 364 | } 365 | } 366 | buff.WriteRune('a') 367 | ctx.SetFileName(buff.String()) 368 | result := ctx.FileName() 369 | if result != string(buff.Bytes()[:1023]) { 370 | t.Fatalf("[TestSetFileName] result = %s, NG, expected = %s", result, buff.String()) 371 | } 372 | } 373 | 374 | func TestContextSeekToTimestamp(t *testing.T) { 375 | ctx, _ := NewContextForInput() 376 | defer ctx.Free() 377 | fixture := fixturePath("sample_mpeg4.mp4") 378 | err := ctx.OpenInput(fixture, nil, nil) 379 | if err != nil { 380 | t.Fatal(err) 381 | } 382 | start := ctx.StartTime() 383 | if err := ctx.SeekToTimestamp(-1, -9223372036854775808, start, start, SeekFlagNone); err != nil { 384 | t.Fatalf("[TestSeekToTimestamp] result(error) = %v, NG, expected no error", err) 385 | } 386 | } 387 | 388 | func TestSampleAspectRatio(t *testing.T) { 389 | ctx, _ := NewContextForInput() 390 | defer ctx.Free() 391 | stream, err := ctx.NewStream() 392 | if err != nil { 393 | t.Fatal(err) 394 | } 395 | expected := avutil.NewRational(1, 5) 396 | stream.SetSampleAspectRatio(expected) 397 | result := stream.SampleAspectRatio() 398 | if result.Numerator() != expected.Numerator() || result.Denominator() != expected.Denominator() { 399 | t.Fatalf("[TestSampleAspectRatio] result = %d/%d, NG, expected = %d/%d", 400 | result.Numerator(), result.Denominator(), expected.Numerator(), expected.Denominator()) 401 | } 402 | } 403 | 404 | func TestRealFrameRate(t *testing.T) { 405 | ctx, _ := NewContextForInput() 406 | defer ctx.Free() 407 | stream, err := ctx.NewStream() 408 | if err != nil { 409 | t.Fatal(err) 410 | } 411 | expected := avutil.NewRational(30, 1) 412 | stream.SetRealFrameRate(expected) 413 | result := stream.RealFrameRate() 414 | if result.Numerator() != expected.Numerator() || result.Denominator() != expected.Denominator() { 415 | t.Fatalf("[TestRealFrameRate] result = %d/%d, NG, expected = %d/%d", 416 | result.Numerator(), result.Denominator(), expected.Numerator(), expected.Denominator()) 417 | } 418 | } 419 | 420 | func TestStreamFirstDTSOK(t *testing.T) { 421 | ctx, _ := NewContextForInput() 422 | defer ctx.Free() 423 | stream, err := ctx.NewStream() 424 | if err != nil { 425 | t.Fatal(err) 426 | } 427 | expected := int64(1500000) 428 | stream.SetFirstDTS(expected) 429 | result := stream.FirstDTS() 430 | if result != expected { 431 | t.Fatalf("[TestStreamFirstDTSOK] result = %d, NG, expected = %d", result, expected) 432 | } 433 | } 434 | 435 | func TestStreamEndPTSDefaultOK(t *testing.T) { 436 | ctx, _ := NewContextForOutput(GuessOutputFromFileName("test.mp4")) 437 | defer ctx.Free() 438 | stream, err := ctx.NewStream() 439 | if err != nil { 440 | t.Fatal(err) 441 | } 442 | result := stream.EndPTS() 443 | expected := avutil.NoPTSValue 444 | if result != expected { 445 | t.Fatalf("[TestStreamEndPTSDefaultOK] result = %d, NG, expected = %d", result, expected) 446 | } 447 | } 448 | 449 | func TestStreamEndPTSOK(t *testing.T) { 450 | iCtx := testOpenInput(t) 451 | defer iCtx.Free() 452 | if size := iCtx.IOContext().Size(); size <= 0 { 453 | t.Fatalf("[TestStreamEndPTSOK] result = %d, NG, expected more than 0", size) 454 | } 455 | oCtx, oStream := testCopy(t, iCtx) 456 | defer oCtx.Free() 457 | pkt := testWritePacket(t, iCtx, oCtx) 458 | defer pkt.Free() 459 | result := oStream.EndPTS() 460 | expected := int64(1024) 461 | if result != expected { 462 | t.Fatalf("[TestStreamEndPTSOK] result = %d, NG, expected = %d", result, expected) 463 | } 464 | } 465 | 466 | func testOpenInput(t *testing.T) *Context { 467 | ctx, _ := NewContextForInput() 468 | if err := ctx.OpenInput(fixturePath("sample_mpeg4.mp4"), nil, nil); err != nil { 469 | t.Fatal(err) 470 | } 471 | if err := ctx.FindStreamInfo(nil); err != nil { 472 | t.Fatal(err) 473 | } 474 | return ctx 475 | } 476 | 477 | func testCopy(t *testing.T, iCtx *Context) (*Context, *Stream) { 478 | ctx, _ := NewContextForOutput(GuessOutputFromFileName("test.mp4")) 479 | iCodecCtx := iCtx.Streams()[0].CodecContext() 480 | stream, err := ctx.NewStreamWithCodec(iCodecCtx.Codec()) 481 | if err != nil { 482 | t.Fatal(err) 483 | } 484 | if err := iCodecCtx.CopyTo(stream.CodecContext()); err != nil { 485 | t.Fatal(err) 486 | } 487 | stream.CodecContext().SetCodecTag(0) 488 | ioCtx, err := OpenIOContext(os.DevNull, IOFlagWrite, nil, nil) 489 | if err != nil { 490 | t.Fatal(err) 491 | } 492 | ctx.SetIOContext(ioCtx) 493 | ctx.WriteHeader(nil) 494 | return ctx, stream 495 | } 496 | 497 | func testWritePacket(t *testing.T, iCtx *Context, oCtx *Context) *avcodec.Packet { 498 | pkt := testNewPacket(t) 499 | iCtx.ReadFrame(pkt) 500 | if err := oCtx.InterleavedWriteFrame(pkt); err != nil { 501 | t.Fatal(err) 502 | } 503 | return pkt 504 | } 505 | 506 | func testNewPacket(t *testing.T) *avcodec.Packet { 507 | pkt, err := avcodec.NewPacket() 508 | if err != nil { 509 | t.Fatal(err) 510 | } 511 | if pkt == nil { 512 | t.Fatalf("Expecting packet") 513 | } 514 | return pkt 515 | } 516 | 517 | func TestGuessFrameRate(t *testing.T) { 518 | ctx, _ := NewContextForInput() 519 | defer ctx.Free() 520 | 521 | fixture := fixturePath("sample_mpeg4.mp4") 522 | if err := ctx.OpenInput(fixture, nil, nil); err != nil { 523 | t.Fatal(err) 524 | } 525 | defer ctx.CloseInput() 526 | 527 | if err := ctx.FindStreamInfo(nil); err != nil { 528 | t.Fatal(err) 529 | } 530 | 531 | if ctx.BitRate() <= 0 { 532 | t.Fatalf("[TestGuessFrameRate] bitrate result = %d, NG, expected greater than 0", ctx.BitRate()) 533 | } 534 | 535 | expected := [][]int{{0, 0}, {30, 1}} 536 | for i, stream := range ctx.Streams() { 537 | result := ctx.GuessFrameRate(stream, nil) 538 | if result.Numerator() != expected[i][0] || result.Denominator() != expected[i][1] { 539 | t.Fatalf("[TestGuessFrameRate] result = %d/%d, NG, expected = %d/%d", 540 | result.Numerator(), result.Denominator(), expected[i][0], expected[i][1]) 541 | } 542 | } 543 | } 544 | 545 | func TestContextDuration(t *testing.T) { 546 | ctx, _ := NewContextForInput() 547 | defer ctx.Free() 548 | 549 | ctx.SetDuration(1000000) 550 | result := ctx.Duration() 551 | if result != 1000000 { 552 | t.Fatalf("[TestContextDuration] result = %d, NG, expected = %d", result, 1000000) 553 | } 554 | } 555 | 556 | func TestContextMaxDelay(t *testing.T) { 557 | ctx, _ := NewContextForInput() 558 | defer ctx.Free() 559 | 560 | ctx.SetMaxDelay(500000) 561 | result := ctx.MaxDelay() 562 | if result != 500000 { 563 | t.Fatalf("[TestContextMaxDelay] result = %d, NG, expected = %d", result, 500000) 564 | } 565 | } 566 | 567 | func TestContextMetaData(t *testing.T) { 568 | fmtCtx, err := NewContextForInput() 569 | if err != nil { 570 | t.Fatal(err) 571 | } 572 | defer fmtCtx.Free() 573 | metadata := fmtCtx.MetaData() 574 | if count := metadata.Count(); count != 0 { 575 | t.Fatalf("Expecting count but got %d", count) 576 | } 577 | if err := metadata.Set("foo", "foo"); err != nil { 578 | t.Fatal(err) 579 | } 580 | if count := metadata.Count(); count != 1 { 581 | t.Fatalf("Expecting count but got %d", count) 582 | } 583 | if count := fmtCtx.MetaData().Count(); count != 1 { 584 | t.Fatalf("Expecting count but got %d", count) 585 | } 586 | if err := metadata.Delete("foo"); err != nil { 587 | t.Fatal(err) 588 | } 589 | if count := metadata.Count(); count != 0 { 590 | t.Fatalf("Expecting count but got %d", count) 591 | } 592 | if err := metadata.Set("bar", "bar"); err != nil { 593 | t.Fatal(err) 594 | } 595 | if count := metadata.Count(); count != 1 { 596 | t.Fatalf("Expecting count but got %d", count) 597 | } 598 | if count := fmtCtx.MetaData().Count(); count != 1 { 599 | t.Fatalf("Expecting count but got %d", count) 600 | } 601 | if err := metadata.Delete("bar"); err != nil { 602 | t.Fatal(err) 603 | } 604 | if count := metadata.Count(); count != 0 { 605 | t.Fatalf("Expecting count but got %d", count) 606 | } 607 | } 608 | 609 | func TestContextSetMetaData(t *testing.T) { 610 | fmtCtx, err := NewContextForInput() 611 | if err != nil { 612 | t.Fatal(err) 613 | } 614 | defer fmtCtx.Free() 615 | if count := fmtCtx.MetaData().Count(); count != 0 { 616 | t.Fatalf("Expecting count but got %d", count) 617 | } 618 | metadata := avutil.NewDictionary() 619 | if err := metadata.Set("foo", "foo"); err != nil { 620 | t.Fatal(err) 621 | } 622 | fmtCtx.SetMetaData(metadata) 623 | if count := fmtCtx.MetaData().Count(); count != 1 { 624 | t.Fatalf("Expecting count but got %d", count) 625 | } 626 | } 627 | 628 | func TestContext_IOOpenCallback(t *testing.T) { 629 | ctx, err := NewContextForInput() 630 | if err != nil { 631 | t.Fatal(err) 632 | } 633 | defer ctx.Free() 634 | cb := ctx.IOOpenCallback() 635 | if cb == nil { 636 | t.Fatalf("[TestContext_IOOpenCallback] callback is nil NG, expected not nil") 637 | } 638 | ctx.SetIOOpenCallback(nil) 639 | nilCB := ctx.IOOpenCallback() 640 | if nilCB != nil { 641 | t.Fatalf("[TestContext_IOOpenCallback] callback is %p NG, expected is nil", nilCB) 642 | } 643 | ctx.SetIOOpenCallback(cb) 644 | resetCB := ctx.IOOpenCallback() 645 | if resetCB == nil { 646 | t.Fatalf("[TestContext_IOOpenCallback] callback is nil NG, expected not nil") 647 | } 648 | if cb != resetCB { 649 | t.Fatalf("[TestContext_IOOpenCallback] callback1 is %p, callback2 is %p NG, expected same", cb, resetCB) 650 | } 651 | } 652 | 653 | func TestContext_IOCloseCallback(t *testing.T) { 654 | ctx, err := NewContextForInput() 655 | if err != nil { 656 | t.Fatal(err) 657 | } 658 | defer ctx.Free() 659 | cb := ctx.IOCloseCallback() 660 | if cb == nil { 661 | t.Fatalf("[TestContext_IOCloseCallback] callback is nil NG, expected not nil") 662 | } 663 | ctx.SetIOCloseCallback(nil) 664 | nilCB := ctx.IOCloseCallback() 665 | if nilCB != nil { 666 | t.Fatalf("[TestContext_IOCloseCallback] callback is %p NG, expected is nil", nilCB) 667 | } 668 | ctx.SetIOCloseCallback(cb) 669 | resetCB := ctx.IOCloseCallback() 670 | if resetCB == nil { 671 | t.Fatalf("[TestContext_IOCloseCallback] callback is nil NG, expected not nil") 672 | } 673 | if cb != resetCB { 674 | t.Fatalf("[TestContext_IOCloseCallback] callback1 is %p, callback2 is %p NG, expected same", cb, resetCB) 675 | } 676 | } 677 | 678 | func TestContextNewFreeLeak1M(t *testing.T) { 679 | before := testMemoryUsed(t) 680 | for i := 0; i < 1000000; i++ { 681 | ctx, err := NewContextForInput() 682 | if err != nil { 683 | t.Fatal(err) 684 | } 685 | ctx.Free() 686 | } 687 | testMemoryLeak(t, before, 50*1024*1024) 688 | } 689 | 690 | func TestIOContextOpenCloseLeak100K(t *testing.T) { 691 | flags := IOFlagWrite 692 | f, err := ioutil.TempFile("", "") 693 | if err != nil { 694 | t.Fatal(err) 695 | } 696 | f.Close() 697 | url := f.Name() 698 | for i := 0; i < 100000; i++ { 699 | ioCtx, err := OpenIOContext(url, flags, nil, nil) 700 | if err != nil { 701 | t.Fatal(err) 702 | } 703 | ioCtx.Close() 704 | } 705 | } 706 | 707 | func TestNumberedSequenceFormat(t *testing.T) { 708 | result := NumberedSequenceFormat("test_%d") 709 | if !result { 710 | t.Fatalf("result is %t, expected %t", result, true) 711 | } 712 | result = NumberedSequenceFormat("test_%04d") 713 | if !result { 714 | t.Fatalf("result is %t, expected %t", result, true) 715 | } 716 | result = NumberedSequenceFormat("test_%s") 717 | if result { 718 | t.Fatalf("result is %t, expected %t", result, false) 719 | } 720 | result = NumberedSequenceFormat("test") 721 | if result { 722 | t.Fatalf("result is %t, expected %t", result, false) 723 | } 724 | } 725 | 726 | func TestFormatNumberedSequence(t *testing.T) { 727 | expected := "test_1" 728 | result, err := FormatNumberedSequence("test_%d", 1) 729 | if err != nil { 730 | t.Fatalf("err is %v, expected not err", err) 731 | } 732 | if result != expected { 733 | t.Fatalf("result is %s, expected %s", result, expected) 734 | } 735 | 736 | expected = "test_0001" 737 | result, err = FormatNumberedSequence("test_%04d", 1) 738 | if err != nil { 739 | t.Fatalf("err is %v, expected not err", err) 740 | } 741 | if result != expected { 742 | t.Fatalf("result is %s, expected %s", result, expected) 743 | } 744 | 745 | expected = "test_-0001" 746 | result, err = FormatNumberedSequence("test_%04d", -1) 747 | if err != nil { 748 | t.Fatalf("err is %v, expected not err", err) 749 | } 750 | if result != expected { 751 | t.Fatalf("result is %s, expected %s", result, expected) 752 | } 753 | 754 | _, err = FormatNumberedSequence("test_%s", 1) 755 | if err == nil { 756 | t.Fatal("err is nil, expected returned err") 757 | } 758 | 759 | _, err = FormatNumberedSequence("test", 1) 760 | if err == nil { 761 | t.Fatal("err is nil, expected returned err") 762 | } 763 | } 764 | 765 | func testMemoryUsed(t *testing.T) uint64 { 766 | p, err := process.NewProcess(int32(os.Getpid())) 767 | if err != nil { 768 | t.Fatal(err) 769 | } 770 | info, err := p.MemoryInfo() 771 | if err != nil { 772 | t.Fatal(err) 773 | } 774 | return info.RSS 775 | } 776 | 777 | func testMemoryLeak(t *testing.T, before uint64, diff uint64) { 778 | after := testMemoryUsed(t) 779 | if after > before && after-before > diff { 780 | t.Fatalf("memory leak detected: %d bytes", after-before) 781 | } 782 | } 783 | 784 | func ExampleShowMediaInfo() { 785 | inputFileName := fixturePath("sample_iPod.m4v") 786 | 787 | // open format (container) context 788 | decFmt, err := NewContextForInput() 789 | if err != nil { 790 | log.Fatalf("Failed to open input context: %v. Run 'make fixture' to fetch the required files", err) 791 | } 792 | 793 | // set some options for opening file 794 | options := avutil.NewDictionary() 795 | defer options.Free() 796 | 797 | // open file for decoding 798 | if err := decFmt.OpenInput(inputFileName, nil, options); err != nil { 799 | log.Fatalf("Failed to open input file: %v", err) 800 | } 801 | defer decFmt.CloseInput() 802 | 803 | // initialize context with stream information 804 | if err := decFmt.FindStreamInfo(nil); err != nil { 805 | log.Fatalf("Failed to find stream info: %v", err) 806 | } 807 | 808 | // show stream info 809 | for _, stream := range decFmt.Streams() { 810 | language := stream.MetaData().Get("language") 811 | streamCtx := stream.CodecContext() 812 | duration := stream.Duration() 813 | codecID := streamCtx.CodecID() 814 | descriptor := avcodec.CodecDescriptorByID(codecID) 815 | switch streamCtx.CodecType() { 816 | case avutil.MediaTypeVideo: 817 | width := streamCtx.Width() 818 | height := streamCtx.Height() 819 | frameRate := stream.AverageFrameRate().Float64() 820 | fmt.Printf("stream %d: %s video, %dx%d in %.2f fps, %v\n", 821 | stream.Index(), 822 | descriptor.Name(), 823 | width, 824 | height, 825 | frameRate, 826 | duration) 827 | case avutil.MediaTypeAudio: 828 | channels := streamCtx.Channels() 829 | sampleRate := streamCtx.SampleRate() 830 | fmt.Printf("stream %d: %s audio, %s, %d channels, %d Hz\n", 831 | stream.Index(), 832 | descriptor.Name(), 833 | language, 834 | channels, 835 | sampleRate) 836 | } 837 | } 838 | // Output: 839 | // stream 0: aac audio, eng, 2 channels, 44100 Hz 840 | // stream 1: h264 video, 320x240 in 10.00 fps, 1m25.5s 841 | } 842 | -------------------------------------------------------------------------------- /avutil/avutil.go: -------------------------------------------------------------------------------- 1 | package avutil 2 | 3 | //go:generate go run hackgenerator.go 4 | 5 | //#include 6 | //#include 7 | //#include 8 | //#include 9 | //#include 10 | //#include 11 | //#include 12 | //#include 13 | //#include 14 | // 15 | //#ifdef AV_LOG_TRACE 16 | //#define GO_AV_LOG_TRACE AV_LOG_TRACE 17 | //#else 18 | //#define GO_AV_LOG_TRACE AV_LOG_DEBUG 19 | //#endif 20 | // 21 | //#ifdef AV_PIX_FMT_XVMC_MPEG2_IDCT 22 | //#define GO_AV_PIX_FMT_XVMC_MPEG2_IDCT AV_PIX_FMT_XVMC_MPEG2_MC 23 | //#else 24 | //#define GO_AV_PIX_FMT_XVMC_MPEG2_IDCT 0 25 | //#endif 26 | // 27 | //#ifdef AV_PIX_FMT_XVMC_MPEG2_MC 28 | //#define GO_AV_PIX_FMT_XVMC_MPEG2_MC AV_PIX_FMT_XVMC_MPEG2_MC 29 | //#else 30 | //#define GO_AV_PIX_FMT_XVMC_MPEG2_MC 0 31 | //#endif 32 | // 33 | //static const AVDictionaryEntry *go_av_dict_next(const AVDictionary *m, const AVDictionaryEntry *prev) 34 | //{ 35 | // return av_dict_get(m, "", prev, AV_DICT_IGNORE_SUFFIX); 36 | //} 37 | // 38 | //static const int go_av_dict_has(const AVDictionary *m, const char *key, int flags) 39 | //{ 40 | // if (av_dict_get(m, key, NULL, flags) != NULL) 41 | // { 42 | // return 1; 43 | // } 44 | // return 0; 45 | //} 46 | // 47 | //static int go_av_expr_parse2(AVExpr **expr, const char *s, const char * const *const_names, int log_offset, void *log_ctx) 48 | //{ 49 | // return av_expr_parse(expr, s, const_names, NULL, NULL, NULL, NULL, log_offset, log_ctx); 50 | //} 51 | // 52 | //static const int go_av_errno_to_error(int e) 53 | //{ 54 | // return AVERROR(e); 55 | //} 56 | // 57 | // #cgo pkg-config: libavutil 58 | import "C" 59 | 60 | import ( 61 | "errors" 62 | "strconv" 63 | "syscall" 64 | "time" 65 | "unsafe" 66 | ) 67 | 68 | var ( 69 | ErrAllocationError = errors.New("allocation error") 70 | ErrInvalidArgumentSize = errors.New("invalid argument size") 71 | ) 72 | 73 | type LogLevel int 74 | 75 | const ( 76 | LogLevelQuiet LogLevel = C.AV_LOG_QUIET 77 | LogLevelPanic LogLevel = C.AV_LOG_PANIC 78 | LogLevelFatal LogLevel = C.AV_LOG_FATAL 79 | LogLevelError LogLevel = C.AV_LOG_ERROR 80 | LogLevelWarning LogLevel = C.AV_LOG_WARNING 81 | LogLevelInfo LogLevel = C.AV_LOG_INFO 82 | LogLevelVerbose LogLevel = C.AV_LOG_VERBOSE 83 | LogLevelDebug LogLevel = C.AV_LOG_DEBUG 84 | LogLevelTrace LogLevel = C.GO_AV_LOG_TRACE 85 | ) 86 | 87 | type MediaType C.enum_AVMediaType 88 | 89 | const ( 90 | MediaTypeUnknown MediaType = C.AVMEDIA_TYPE_UNKNOWN 91 | MediaTypeVideo MediaType = C.AVMEDIA_TYPE_VIDEO 92 | MediaTypeAudio MediaType = C.AVMEDIA_TYPE_AUDIO 93 | MediaTypeData MediaType = C.AVMEDIA_TYPE_DATA 94 | MediaTypeSubtitle MediaType = C.AVMEDIA_TYPE_SUBTITLE 95 | MediaTypeAttachment MediaType = C.AVMEDIA_TYPE_ATTACHMENT 96 | ) 97 | 98 | type PictureType C.enum_AVPictureType 99 | 100 | const ( 101 | PictureTypeNone PictureType = C.AV_PICTURE_TYPE_NONE 102 | PictureTypeI PictureType = C.AV_PICTURE_TYPE_I 103 | PictureTypeP PictureType = C.AV_PICTURE_TYPE_P 104 | PictureTypeB PictureType = C.AV_PICTURE_TYPE_B 105 | PictureTypeS PictureType = C.AV_PICTURE_TYPE_S 106 | PictureTypeSI PictureType = C.AV_PICTURE_TYPE_SI 107 | PictureTypeSP PictureType = C.AV_PICTURE_TYPE_SP 108 | PictureTypeBI PictureType = C.AV_PICTURE_TYPE_BI 109 | ) 110 | 111 | type ChromaLocation C.enum_AVChromaLocation 112 | 113 | const ( 114 | ChromaLocationUnspecified ChromaLocation = C.AVCHROMA_LOC_UNSPECIFIED 115 | ChromaLocationLeft ChromaLocation = C.AVCHROMA_LOC_LEFT 116 | ChromaLocationCenter ChromaLocation = C.AVCHROMA_LOC_CENTER 117 | ChromaLocationTopLeft ChromaLocation = C.AVCHROMA_LOC_TOPLEFT 118 | ChromaLocationTop ChromaLocation = C.AVCHROMA_LOC_TOP 119 | ChromaLocationBottomLeft ChromaLocation = C.AVCHROMA_LOC_BOTTOMLEFT 120 | ChromaLocationBottom ChromaLocation = C.AVCHROMA_LOC_BOTTOM 121 | ) 122 | 123 | type ErrorCode int 124 | 125 | type OptionSearchFlags int 126 | 127 | const ( 128 | OptionSearchChildren OptionSearchFlags = C.AV_OPT_SEARCH_CHILDREN 129 | OptionSearchFakeObj OptionSearchFlags = C.AV_OPT_SEARCH_FAKE_OBJ 130 | ) 131 | 132 | type LossFlags int 133 | 134 | const ( 135 | LossFlagNone LossFlags = 0 136 | LossFlagResolution LossFlags = C.FF_LOSS_RESOLUTION 137 | LossFlagDepth LossFlags = C.FF_LOSS_DEPTH 138 | LossFlagColorspace LossFlags = C.FF_LOSS_COLORSPACE 139 | LossFlagAlpha LossFlags = C.FF_LOSS_ALPHA 140 | LossFlagColorquant LossFlags = C.FF_LOSS_COLORQUANT 141 | LossFlagChroma LossFlags = C.FF_LOSS_CHROMA 142 | LossFlagAll LossFlags = -1 143 | ) 144 | 145 | func init() { 146 | SetLogLevel(LogLevelQuiet) 147 | } 148 | 149 | func Version() (int, int, int) { 150 | return int(C.LIBAVUTIL_VERSION_MAJOR), int(C.LIBAVUTIL_VERSION_MINOR), int(C.LIBAVUTIL_VERSION_MICRO) 151 | } 152 | 153 | func SetLogLevel(level LogLevel) { 154 | C.av_log_set_level(C.int(level)) 155 | } 156 | 157 | type SampleFormat C.enum_AVSampleFormat 158 | 159 | const ( 160 | SampleFormatNone SampleFormat = C.AV_SAMPLE_FMT_NONE 161 | SampleFormatU8 SampleFormat = C.AV_SAMPLE_FMT_U8 162 | SampleFormatS16 SampleFormat = C.AV_SAMPLE_FMT_S16 163 | SampleFormatS32 SampleFormat = C.AV_SAMPLE_FMT_S32 164 | SampleFormatFLT SampleFormat = C.AV_SAMPLE_FMT_FLT 165 | SampleFormatDBL SampleFormat = C.AV_SAMPLE_FMT_DBL 166 | SampleFormatU8P SampleFormat = C.AV_SAMPLE_FMT_U8P 167 | SampleFormatS16P SampleFormat = C.AV_SAMPLE_FMT_S16P 168 | SampleFormatS32P SampleFormat = C.AV_SAMPLE_FMT_S32P 169 | SampleFormatFLTP SampleFormat = C.AV_SAMPLE_FMT_FLTP 170 | SampleFormatDBLP SampleFormat = C.AV_SAMPLE_FMT_DBLP 171 | ) 172 | 173 | func FindSampleFormatByName(name string) (SampleFormat, bool) { 174 | cName := C.CString(name) 175 | defer C.free(unsafe.Pointer(cName)) 176 | cSampleFormat := C.av_get_sample_fmt(cName) 177 | return SampleFormat(cSampleFormat), (cSampleFormat != C.AV_SAMPLE_FMT_NONE) 178 | } 179 | 180 | func (sfmt SampleFormat) Name() string { 181 | str, _ := sfmt.NameOk() 182 | return str 183 | } 184 | 185 | func (sfmt SampleFormat) NameOk() (string, bool) { 186 | return cStringToStringOk(C.av_get_sample_fmt_name((C.enum_AVSampleFormat)(sfmt))) 187 | } 188 | 189 | type PixelFormat C.enum_AVPixelFormat 190 | 191 | const ( 192 | PixelFormatNone PixelFormat = C.AV_PIX_FMT_NONE 193 | ) 194 | 195 | func FindPixelFormatByName(name string) (PixelFormat, bool) { 196 | cName := C.CString(name) 197 | defer C.free(unsafe.Pointer(cName)) 198 | cPixelFormat := C.av_get_pix_fmt(cName) 199 | return PixelFormat(cPixelFormat), (cPixelFormat != C.AV_PIX_FMT_NONE) 200 | } 201 | 202 | func (pfmt PixelFormat) Name() string { 203 | str, _ := pfmt.NameOk() 204 | return str 205 | } 206 | 207 | func (pfmt PixelFormat) NameOk() (string, bool) { 208 | return cStringToStringOk(C.av_get_pix_fmt_name((C.enum_AVPixelFormat)(pfmt))) 209 | } 210 | 211 | type PixelFormatDescriptor struct { 212 | CAVPixFmtDescriptor *C.AVPixFmtDescriptor 213 | } 214 | 215 | func NewPixelFormatDescriptorFromC(cCtx unsafe.Pointer) *PixelFormatDescriptor { 216 | return &PixelFormatDescriptor{CAVPixFmtDescriptor: (*C.AVPixFmtDescriptor)(cCtx)} 217 | } 218 | 219 | func FindPixelFormatDescriptorByPixelFormat(pixelFormat PixelFormat) *PixelFormatDescriptor { 220 | cDescriptor := C.av_pix_fmt_desc_get(C.enum_AVPixelFormat(pixelFormat)) 221 | if cDescriptor == nil { 222 | return nil 223 | } 224 | return NewPixelFormatDescriptorFromC(unsafe.Pointer(cDescriptor)) 225 | } 226 | 227 | func (d *PixelFormatDescriptor) ComponentCount() int { 228 | return int(d.CAVPixFmtDescriptor.nb_components) 229 | } 230 | 231 | type ChannelLayout uint64 232 | 233 | const ( 234 | ChannelLayoutMono ChannelLayout = C.AV_CH_LAYOUT_MONO 235 | ChannelLayoutStereo ChannelLayout = C.AV_CH_LAYOUT_STEREO 236 | ChannelLayout2Point1 ChannelLayout = C.AV_CH_LAYOUT_2POINT1 237 | ChannelLayout21 ChannelLayout = C.AV_CH_LAYOUT_2_1 238 | ChannelLayoutSurround ChannelLayout = C.AV_CH_LAYOUT_SURROUND 239 | ChannelLayout3Point1 ChannelLayout = C.AV_CH_LAYOUT_3POINT1 240 | ChannelLayout4Point0 ChannelLayout = C.AV_CH_LAYOUT_4POINT0 241 | ChannelLayout4Point1 ChannelLayout = C.AV_CH_LAYOUT_4POINT1 242 | ChannelLayout22 ChannelLayout = C.AV_CH_LAYOUT_2_2 243 | ChannelLayoutQuad ChannelLayout = C.AV_CH_LAYOUT_QUAD 244 | ChannelLayout5Point0 ChannelLayout = C.AV_CH_LAYOUT_5POINT0 245 | ChannelLayout5Point1 ChannelLayout = C.AV_CH_LAYOUT_5POINT1 246 | ChannelLayout5Point0Back ChannelLayout = C.AV_CH_LAYOUT_5POINT0_BACK 247 | ChannelLayout5Point1Back ChannelLayout = C.AV_CH_LAYOUT_5POINT1_BACK 248 | ChannelLayout6Point0 ChannelLayout = C.AV_CH_LAYOUT_6POINT0 249 | ChannelLayout6Point0Front ChannelLayout = C.AV_CH_LAYOUT_6POINT0_FRONT 250 | ChannelLayoutHexagonal ChannelLayout = C.AV_CH_LAYOUT_HEXAGONAL 251 | ChannelLayout6Point1 ChannelLayout = C.AV_CH_LAYOUT_6POINT1 252 | ChannelLayout6Point1Back ChannelLayout = C.AV_CH_LAYOUT_6POINT1_BACK 253 | ChannelLayout6Point1Front ChannelLayout = C.AV_CH_LAYOUT_6POINT1_FRONT 254 | ChannelLayout7Point0 ChannelLayout = C.AV_CH_LAYOUT_7POINT0 255 | ChannelLayout7Point0Front ChannelLayout = C.AV_CH_LAYOUT_7POINT0_FRONT 256 | ChannelLayout7Point1 ChannelLayout = C.AV_CH_LAYOUT_7POINT1 257 | ChannelLayout7Point1Wide ChannelLayout = C.AV_CH_LAYOUT_7POINT1_WIDE 258 | ChannelLayout7Point1WideBack ChannelLayout = C.AV_CH_LAYOUT_7POINT1_WIDE_BACK 259 | ChannelLayoutOctagonal ChannelLayout = C.AV_CH_LAYOUT_OCTAGONAL 260 | ChannelLayoutStereoDownmix ChannelLayout = C.AV_CH_LAYOUT_STEREO_DOWNMIX 261 | ) 262 | 263 | func FindChannelLayoutByName(name string) (ChannelLayout, bool) { 264 | cName := C.CString(name) 265 | defer C.free(unsafe.Pointer(cName)) 266 | cChannelLayout := C.av_get_channel_layout(cName) 267 | return ChannelLayout(cChannelLayout), (cChannelLayout != 0) 268 | } 269 | 270 | func FindDefaultChannelLayout(numberOfChannels int) (ChannelLayout, bool) { 271 | cl := C.av_get_default_channel_layout(C.int(numberOfChannels)) 272 | if cl <= 0 { 273 | return 0, false 274 | } 275 | return ChannelLayout(cl), true 276 | } 277 | 278 | func (cl ChannelLayout) NumberOfChannels() int { 279 | return int(C.av_get_channel_layout_nb_channels((C.uint64_t)(cl))) 280 | } 281 | 282 | func (cl ChannelLayout) Name() string { 283 | str, _ := cl.NameOk() 284 | return str 285 | } 286 | 287 | func (cl ChannelLayout) NameOk() (string, bool) { 288 | for index := C.unsigned(0); ; index++ { 289 | var cCL C.uint64_t 290 | var cName *C.char 291 | if C.av_get_standard_channel_layout(index, &cCL, &cName) != 0 { 292 | break 293 | } 294 | if ChannelLayout(cCL) == cl { 295 | return cStringToStringOk(cName) 296 | } 297 | } 298 | return "", false 299 | } 300 | 301 | func (cl ChannelLayout) DescriptionOk() (string, bool) { 302 | return cStringToStringOk(C.av_get_channel_description((C.uint64_t)(cl))) 303 | } 304 | 305 | func ChannelLayouts() []ChannelLayout { 306 | var cls []ChannelLayout 307 | for index := C.unsigned(0); ; index++ { 308 | var cCL C.uint64_t 309 | if C.av_get_standard_channel_layout(index, &cCL, nil) != 0 { 310 | break 311 | } 312 | cls = append(cls, ChannelLayout(cCL)) 313 | } 314 | return cls 315 | } 316 | 317 | type Rational struct { 318 | CAVRational C.AVRational 319 | } 320 | 321 | func NewRational(numerator, denominator int) *Rational { 322 | r := &Rational{} 323 | r.CAVRational.num = C.int(numerator) 324 | r.CAVRational.den = C.int(denominator) 325 | return r 326 | } 327 | 328 | var zeroRational = NewRational(0, 1) 329 | 330 | func NewRationalFromC(cRational unsafe.Pointer) *Rational { 331 | rational := (*C.AVRational)(cRational) 332 | return NewRational(int(rational.num), int(rational.den)) 333 | } 334 | 335 | func (r *Rational) String() string { 336 | return strconv.Itoa(r.Numerator()) + ":" + strconv.Itoa(r.Denominator()) 337 | } 338 | 339 | func (r *Rational) Numerator() int { 340 | return int(r.CAVRational.num) 341 | } 342 | 343 | func (r *Rational) SetNumerator(numerator int) { 344 | r.CAVRational.num = (C.int)(numerator) 345 | } 346 | 347 | func (r *Rational) Denominator() int { 348 | return int(r.CAVRational.den) 349 | } 350 | 351 | func (r *Rational) SetDenominator(denominator int) { 352 | r.CAVRational.den = (C.int)(denominator) 353 | } 354 | 355 | func (r *Rational) Add(r2 *Rational) { 356 | r.CAVRational = C.av_add_q(r.CAVRational, r2.CAVRational) 357 | } 358 | 359 | func (r *Rational) Sub(r2 *Rational) { 360 | r.CAVRational = C.av_sub_q(r.CAVRational, r2.CAVRational) 361 | } 362 | 363 | func (r *Rational) Mul(r2 *Rational) { 364 | r.CAVRational = C.av_mul_q(r.CAVRational, r2.CAVRational) 365 | } 366 | 367 | func (r *Rational) Div(r2 *Rational) { 368 | r.CAVRational = C.av_div_q(r.CAVRational, r2.CAVRational) 369 | } 370 | 371 | func (r *Rational) Invert() { 372 | r.CAVRational = C.av_inv_q(r.CAVRational) 373 | } 374 | 375 | func (r *Rational) Reduce() { 376 | r.Add(zeroRational) 377 | } 378 | 379 | func (r *Rational) Compare(r2 *Rational) (int, bool) { 380 | res := C.av_cmp_q(r.CAVRational, r2.CAVRational) 381 | switch res { 382 | case 0, 1, -1: 383 | return int(res), true 384 | } 385 | return 0, false 386 | } 387 | 388 | func (r *Rational) Nearer(r2 *Rational, r3 *Rational) int { 389 | return int(C.av_nearer_q(r.CAVRational, r2.CAVRational, r3.CAVRational)) 390 | } 391 | 392 | func (r *Rational) Nearest(rs []*Rational) *Rational { 393 | var nearest *Rational 394 | for _, r2 := range rs { 395 | if nearest == nil { 396 | nearest = r2 397 | } else { 398 | res := C.av_nearer_q(r.CAVRational, r2.CAVRational, nearest.CAVRational) 399 | if res > 0 { 400 | nearest = r2 401 | } 402 | } 403 | } 404 | return nearest 405 | } 406 | 407 | func (r *Rational) Copy() *Rational { 408 | r2 := &Rational{} 409 | r2.CAVRational.num = r.CAVRational.num 410 | r2.CAVRational.den = r.CAVRational.den 411 | return r2 412 | } 413 | 414 | func (r *Rational) Float64() float64 { 415 | return float64(r.CAVRational.num) / float64(r.CAVRational.den) 416 | } 417 | 418 | var StandardTimeBase = NewRational(1, C.AV_TIME_BASE) 419 | 420 | type Time struct { 421 | Point int64 422 | Base *Rational 423 | } 424 | 425 | func NewTime(point int64, base *Rational) *Time { 426 | return &Time{ 427 | Point: point, 428 | Base: base, 429 | } 430 | } 431 | 432 | func (t *Time) Valid() bool { 433 | return time.Duration(t.Base.Denominator()) > 0 434 | } 435 | 436 | func (t *Time) Duration() (time.Duration, bool) { 437 | if !t.Valid() { 438 | return 0, false 439 | } 440 | x := t.Point * int64(t.Base.Numerator()) 441 | d := time.Duration(x) * time.Second / time.Duration(t.Base.Denominator()) 442 | return d, true 443 | } 444 | 445 | func ErrnoErrorCode(e syscall.Errno) ErrorCode { 446 | return ErrorCode(C.go_av_errno_to_error(C.int(e))) 447 | } 448 | 449 | type Error struct { 450 | code ErrorCode 451 | err error 452 | } 453 | 454 | func NewErrorFromCode(code ErrorCode) *Error { 455 | return &Error{ 456 | code: code, 457 | err: errors.New(strError(C.int(code))), 458 | } 459 | } 460 | 461 | func (e *Error) Code() ErrorCode { 462 | return e.code 463 | } 464 | 465 | func (e *Error) Error() string { 466 | return e.err.Error() 467 | } 468 | 469 | func strError(code C.int) string { 470 | size := C.size_t(256) 471 | buf := (*C.char)(C.av_mallocz(size)) 472 | defer C.av_free(unsafe.Pointer(buf)) 473 | if C.av_strerror(code, buf, size-1) == 0 { 474 | return C.GoString(buf) 475 | } 476 | return "Unknown error" 477 | } 478 | 479 | var _ error = (*Error)(nil) 480 | 481 | type Dictionary struct { 482 | CAVDictionary **C.AVDictionary 483 | pCAVDictionary *C.AVDictionary 484 | } 485 | 486 | func NewDictionary() *Dictionary { 487 | return NewDictionaryFromC(nil) 488 | } 489 | 490 | func NewDictionaryFromC(cDictionary unsafe.Pointer) *Dictionary { 491 | return &Dictionary{CAVDictionary: (**C.AVDictionary)(cDictionary)} 492 | } 493 | 494 | func (dict *Dictionary) Free() { 495 | C.av_dict_free(dict.pointer()) 496 | } 497 | 498 | func (dict *Dictionary) Pointer() unsafe.Pointer { 499 | return unsafe.Pointer(dict.pointer()) 500 | } 501 | 502 | func (dict *Dictionary) pointer() **C.AVDictionary { 503 | if dict.CAVDictionary != nil { 504 | return dict.CAVDictionary 505 | } 506 | return &dict.pCAVDictionary 507 | } 508 | 509 | func (dict *Dictionary) Value() unsafe.Pointer { 510 | return unsafe.Pointer(dict.value()) 511 | } 512 | 513 | func (dict *Dictionary) value() *C.AVDictionary { 514 | if dict.CAVDictionary != nil { 515 | return *dict.CAVDictionary 516 | } 517 | return dict.pCAVDictionary 518 | } 519 | 520 | func (dict *Dictionary) has(key string, flags C.int) bool { 521 | cKey := C.CString(key) 522 | defer C.free(unsafe.Pointer(cKey)) 523 | has := C.go_av_dict_has(dict.value(), cKey, flags) 524 | if has == 0 { 525 | return false 526 | } 527 | return true 528 | } 529 | 530 | func (dict *Dictionary) Has(key string) bool { 531 | return dict.has(key, C.AV_DICT_MATCH_CASE) 532 | } 533 | 534 | func (dict *Dictionary) HasInsensitive(key string) bool { 535 | return dict.has(key, 0) 536 | } 537 | 538 | func (dict *Dictionary) get(key string, flags C.int) (string, bool) { 539 | cKey := C.CString(key) 540 | defer C.free(unsafe.Pointer(cKey)) 541 | entry := C.av_dict_get(dict.value(), cKey, nil, flags) 542 | if entry == nil { 543 | return "", false 544 | } 545 | return C.GoString(entry.value), true 546 | } 547 | 548 | func (dict *Dictionary) Get(key string) string { 549 | str, _ := dict.GetOk(key) 550 | return str 551 | } 552 | 553 | func (dict *Dictionary) GetOk(key string) (string, bool) { 554 | return dict.get(key, C.AV_DICT_MATCH_CASE) 555 | } 556 | 557 | func (dict *Dictionary) GetInsensitive(key string) string { 558 | str, _ := dict.GetInsensitiveOk(key) 559 | return str 560 | } 561 | 562 | func (dict *Dictionary) GetInsensitiveOk(key string) (string, bool) { 563 | return dict.get(key, 0) 564 | } 565 | 566 | func (dict *Dictionary) set(key, value string, flags C.int) error { 567 | cKey := C.CString(key) 568 | defer C.free(unsafe.Pointer(cKey)) 569 | cValue := C.CString(value) 570 | defer C.free(unsafe.Pointer(cValue)) 571 | code := C.av_dict_set(dict.pointer(), cKey, cValue, flags) 572 | if code < 0 { 573 | return NewErrorFromCode(ErrorCode(code)) 574 | } 575 | return nil 576 | } 577 | 578 | func (dict *Dictionary) Set(key, value string) error { 579 | return dict.set(key, value, C.AV_DICT_MATCH_CASE) 580 | } 581 | 582 | func (dict *Dictionary) Delete(key string) error { 583 | cKey := C.CString(key) 584 | defer C.free(unsafe.Pointer(cKey)) 585 | code := C.av_dict_set(dict.pointer(), cKey, nil, 0) 586 | if code < 0 { 587 | return NewErrorFromCode(ErrorCode(code)) 588 | } 589 | return nil 590 | } 591 | 592 | func (dict *Dictionary) SetInsensitive(key, value string) error { 593 | return dict.set(key, value, 0) 594 | } 595 | 596 | func (dict *Dictionary) Count() int { 597 | return int(C.av_dict_count(dict.value())) 598 | } 599 | 600 | func (dict *Dictionary) Keys() []string { 601 | count := dict.Count() 602 | if count <= 0 { 603 | return nil 604 | } 605 | keys := make([]string, 0, count) 606 | var entry *C.AVDictionaryEntry 607 | for { 608 | entry = C.go_av_dict_next(dict.value(), entry) 609 | if entry == nil { 610 | break 611 | } 612 | keys = append(keys, C.GoString(entry.key)) 613 | } 614 | return keys 615 | } 616 | 617 | func (dict *Dictionary) Values() []string { 618 | count := dict.Count() 619 | if count <= 0 { 620 | return nil 621 | } 622 | values := make([]string, 0, count) 623 | var entry *C.AVDictionaryEntry 624 | for { 625 | entry = C.go_av_dict_next(dict.value(), entry) 626 | if entry == nil { 627 | break 628 | } 629 | values = append(values, C.GoString(entry.value)) 630 | } 631 | return values 632 | } 633 | 634 | func (dict *Dictionary) Map() map[string]string { 635 | count := dict.Count() 636 | if count <= 0 { 637 | return nil 638 | } 639 | m := make(map[string]string, count) 640 | var entry *C.AVDictionaryEntry 641 | for { 642 | entry = C.go_av_dict_next(dict.value(), entry) 643 | if entry == nil { 644 | break 645 | } 646 | m[C.GoString(entry.key)] = C.GoString(entry.value) 647 | } 648 | return m 649 | } 650 | 651 | func (dict *Dictionary) Copy() *Dictionary { 652 | newDict := NewDictionary() 653 | C.av_dict_copy(newDict.pointer(), dict.value(), C.AV_DICT_MATCH_CASE) 654 | return newDict 655 | } 656 | 657 | func (dict *Dictionary) String(keyValSep, pairsSep byte) (string, error) { 658 | buf := (*C.char)(nil) 659 | defer C.av_freep(unsafe.Pointer(&buf)) 660 | code := C.av_dict_get_string(dict.value(), &buf, C.char(keyValSep), C.char(pairsSep)) 661 | if code < 0 { 662 | return "", NewErrorFromCode(ErrorCode(code)) 663 | } 664 | return C.GoString(buf), nil 665 | } 666 | 667 | func cStringToStringOk(cStr *C.char) (string, bool) { 668 | if cStr == nil { 669 | return "", false 670 | } 671 | return C.GoString(cStr), true 672 | } 673 | 674 | type Option struct { 675 | CAVOption *C.struct_AVOption 676 | } 677 | 678 | func NewOptionFromC(cOption unsafe.Pointer) *Option { 679 | return &Option{CAVOption: (*C.struct_AVOption)(cOption)} 680 | } 681 | 682 | func (o *Option) Name() string { 683 | str, _ := o.NameOk() 684 | return str 685 | } 686 | 687 | func (o *Option) NameOk() (string, bool) { 688 | return cStringToStringOk(o.CAVOption.name) 689 | } 690 | 691 | func (o *Option) Help() string { 692 | str, _ := o.HelpOk() 693 | return str 694 | } 695 | 696 | func (o *Option) HelpOk() (string, bool) { 697 | return cStringToStringOk(o.CAVOption.help) 698 | } 699 | 700 | type Class struct { 701 | CAVClass *C.AVClass 702 | } 703 | 704 | func NewClassFromC(cClass unsafe.Pointer) *Class { 705 | return &Class{CAVClass: (*C.AVClass)(cClass)} 706 | } 707 | 708 | func (c *Class) Name() string { 709 | str, _ := c.NameOk() 710 | return str 711 | } 712 | 713 | func (c *Class) NameOk() (string, bool) { 714 | return cStringToStringOk(c.CAVClass.class_name) 715 | } 716 | 717 | func (c *Class) Options() []*Option { 718 | var cur *C.struct_AVOption 719 | var options []*Option 720 | for { 721 | cur = C.av_opt_next(unsafe.Pointer(&c.CAVClass), cur) 722 | if cur == nil { 723 | break 724 | } 725 | options = append(options, NewOptionFromC(unsafe.Pointer(cur))) 726 | } 727 | return options 728 | } 729 | 730 | func (c *Class) ChildrenClasses() []*Class { 731 | var child *C.AVClass 732 | var children []*Class 733 | for { 734 | child = C.av_opt_child_class_next(c.CAVClass, child) 735 | if child == nil { 736 | break 737 | } 738 | children = append(children, NewClassFromC(unsafe.Pointer(child))) 739 | } 740 | return children 741 | } 742 | 743 | type Frame struct { 744 | CAVFrame *C.AVFrame 745 | } 746 | 747 | func NewFrame() (*Frame, error) { 748 | cFrame := C.av_frame_alloc() 749 | if cFrame == nil { 750 | return nil, ErrAllocationError 751 | } 752 | return NewFrameFromC(unsafe.Pointer(cFrame)), nil 753 | } 754 | 755 | func NewFrameFromC(cFrame unsafe.Pointer) *Frame { 756 | return &Frame{CAVFrame: (*C.AVFrame)(cFrame)} 757 | } 758 | 759 | func (f *Frame) Free() { 760 | if f.CAVFrame != nil { 761 | C.av_frame_free(&f.CAVFrame) 762 | } 763 | } 764 | 765 | func (f *Frame) Ref(dst *Frame) error { 766 | code := C.av_frame_ref(dst.CAVFrame, f.CAVFrame) 767 | if code < 0 { 768 | return NewErrorFromCode(ErrorCode(code)) 769 | } 770 | return nil 771 | } 772 | 773 | func (f *Frame) Unref() { 774 | C.av_frame_unref(f.CAVFrame) 775 | } 776 | 777 | func (f *Frame) GetBuffer() error { 778 | return f.GetBufferWithAlignment(32) 779 | } 780 | 781 | func (f *Frame) GetBufferWithAlignment(alignment int) error { 782 | code := C.av_frame_get_buffer(f.CAVFrame, C.int(alignment)) 783 | if code < 0 { 784 | return NewErrorFromCode(ErrorCode(code)) 785 | } 786 | return nil 787 | } 788 | 789 | func (f *Frame) Data(index int) unsafe.Pointer { 790 | return unsafe.Pointer(f.CAVFrame.data[index]) 791 | } 792 | 793 | func (f *Frame) SetData(index int, data unsafe.Pointer) { 794 | f.CAVFrame.data[index] = (*C.uint8_t)(data) 795 | } 796 | 797 | func (f *Frame) LineSize(index int) int { 798 | return int(f.CAVFrame.linesize[index]) 799 | } 800 | 801 | func (f *Frame) SetLineSize(index int, lineSize int) { 802 | f.CAVFrame.linesize[index] = (C.int)(lineSize) 803 | } 804 | 805 | func (f *Frame) ExtendedData() unsafe.Pointer { 806 | return unsafe.Pointer(f.CAVFrame.extended_data) 807 | } 808 | 809 | func (f *Frame) SetExtendedData(data unsafe.Pointer) { 810 | f.CAVFrame.extended_data = (**C.uint8_t)(data) 811 | } 812 | 813 | func (f *Frame) Width() int { 814 | return int(f.CAVFrame.width) 815 | } 816 | 817 | func (f *Frame) SetWidth(width int) { 818 | f.CAVFrame.width = (C.int)(width) 819 | } 820 | 821 | func (f *Frame) Height() int { 822 | return int(f.CAVFrame.height) 823 | } 824 | 825 | func (f *Frame) SetHeight(height int) { 826 | f.CAVFrame.height = (C.int)(height) 827 | } 828 | 829 | func (f *Frame) NumberOfSamples() int { 830 | return int(f.CAVFrame.nb_samples) 831 | } 832 | 833 | func (f *Frame) SetNumberOfSamples(samples int) { 834 | f.CAVFrame.nb_samples = (C.int)(samples) 835 | } 836 | 837 | func (f *Frame) PixelFormat() PixelFormat { 838 | return PixelFormat(f.CAVFrame.format) 839 | } 840 | 841 | func (f *Frame) SetPixelFormat(format PixelFormat) { 842 | f.CAVFrame.format = (C.int)(format) 843 | } 844 | 845 | func (f *Frame) KeyFrame() bool { 846 | return f.CAVFrame.key_frame != (C.int)(0) 847 | } 848 | 849 | func (f *Frame) SetKeyFrame(keyFrame bool) { 850 | if keyFrame { 851 | f.CAVFrame.key_frame = 1 852 | } else { 853 | f.CAVFrame.key_frame = 0 854 | } 855 | } 856 | 857 | func (f *Frame) PictureType() PictureType { 858 | return PictureType(f.CAVFrame.pict_type) 859 | } 860 | 861 | func (f *Frame) SetPictureType(ptype PictureType) { 862 | f.CAVFrame.pict_type = (C.enum_AVPictureType)(ptype) 863 | } 864 | 865 | func (f *Frame) SampleAspectRatio() *Rational { 866 | return NewRationalFromC(unsafe.Pointer(&f.CAVFrame.sample_aspect_ratio)) 867 | } 868 | 869 | func (f *Frame) SetSampleAspectRatio(aspectRatio *Rational) { 870 | f.CAVFrame.sample_aspect_ratio.num = (C.int)(aspectRatio.Numerator()) 871 | f.CAVFrame.sample_aspect_ratio.den = (C.int)(aspectRatio.Denominator()) 872 | } 873 | 874 | func (f *Frame) PTS() int64 { 875 | return int64(f.CAVFrame.pts) 876 | } 877 | 878 | func (f *Frame) SetPTS(pts int64) { 879 | f.CAVFrame.pts = (C.int64_t)(pts) 880 | } 881 | 882 | func (f *Frame) PacketPTS() int64 { 883 | return int64(f.CAVFrame.pkt_pts) 884 | } 885 | 886 | func (f *Frame) SetPacketPTS(pts int64) { 887 | f.CAVFrame.pkt_pts = (C.int64_t)(pts) 888 | } 889 | 890 | func (f *Frame) PacketDTS() int64 { 891 | return int64(f.CAVFrame.pkt_dts) 892 | } 893 | 894 | func (f *Frame) SetPacketDTS(dts int64) { 895 | f.CAVFrame.pkt_dts = (C.int64_t)(dts) 896 | } 897 | 898 | func (f *Frame) CodedPictureNumber() int { 899 | return int(f.CAVFrame.coded_picture_number) 900 | } 901 | 902 | func (f *Frame) SetCodedPictureNumber(number int) { 903 | f.CAVFrame.coded_picture_number = (C.int)(number) 904 | } 905 | 906 | func (f *Frame) DisplayPictureNumber() int { 907 | return int(f.CAVFrame.display_picture_number) 908 | } 909 | 910 | func (f *Frame) SetDisplayPictureNumber(number int) { 911 | f.CAVFrame.display_picture_number = (C.int)(number) 912 | } 913 | 914 | func (f *Frame) Quality() int { 915 | return int(f.CAVFrame.quality) 916 | } 917 | 918 | func (f *Frame) SetQuality(quality int) { 919 | f.CAVFrame.quality = (C.int)(quality) 920 | } 921 | 922 | func (f *Frame) Opaque() unsafe.Pointer { 923 | return unsafe.Pointer(f.CAVFrame.opaque) 924 | } 925 | 926 | func (f *Frame) SetOpaque(opaque unsafe.Pointer) { 927 | f.CAVFrame.opaque = opaque 928 | } 929 | 930 | func (f *Frame) Metadata() *Dictionary { 931 | dict := C.av_frame_get_metadata(f.CAVFrame) 932 | if dict == nil { 933 | return nil 934 | } 935 | return NewDictionaryFromC(unsafe.Pointer(&dict)) 936 | } 937 | 938 | func (f *Frame) SetMetadata(dict *Dictionary) { 939 | if dict == nil { 940 | C.av_frame_set_metadata(f.CAVFrame, nil) 941 | return 942 | } 943 | C.av_frame_set_metadata(f.CAVFrame, dict.value()) 944 | } 945 | 946 | func (f *Frame) BestEffortTimestamp() int64 { 947 | return int64(C.av_frame_get_best_effort_timestamp(f.CAVFrame)) 948 | } 949 | 950 | func (f *Frame) PacketDuration() int64 { 951 | return int64(C.av_frame_get_pkt_duration(f.CAVFrame)) 952 | } 953 | 954 | type OptionAccessor struct { 955 | obj unsafe.Pointer 956 | fake bool 957 | } 958 | 959 | func NewOptionAccessor(obj unsafe.Pointer, fake bool) *OptionAccessor { 960 | return &OptionAccessor{obj: obj, fake: fake} 961 | } 962 | 963 | func (oa *OptionAccessor) GetOption(name string) (string, bool, error) { 964 | return oa.GetOptionWithFlags(name, OptionSearchChildren) 965 | } 966 | 967 | func (oa *OptionAccessor) GetOptionWithFlags(name string, flags OptionSearchFlags) (string, bool, error) { 968 | var cOut *C.uint8_t 969 | cName := C.CString(name) 970 | defer C.free(unsafe.Pointer(cName)) 971 | searchFlags := oa.searchFlags(flags) 972 | code := C.av_opt_get(oa.obj, cName, searchFlags, &cOut) 973 | if code < 0 { 974 | return "", false, getOptionError(code) 975 | } 976 | defer C.av_free(unsafe.Pointer(cOut)) 977 | cStr := (*C.char)(unsafe.Pointer(cOut)) 978 | return C.GoString(cStr), true, nil 979 | } 980 | 981 | func (oa *OptionAccessor) GetInt64Option(name string) (int64, bool, error) { 982 | return oa.GetInt64OptionWithFlags(name, OptionSearchChildren) 983 | } 984 | 985 | func (oa *OptionAccessor) GetInt64OptionWithFlags(name string, flags OptionSearchFlags) (int64, bool, error) { 986 | var cOut C.int64_t 987 | cName := C.CString(name) 988 | defer C.free(unsafe.Pointer(cName)) 989 | searchFlags := oa.searchFlags(flags) 990 | code := C.av_opt_get_int(oa.obj, cName, searchFlags, &cOut) 991 | if code < 0 { 992 | return 0, false, getOptionError(code) 993 | } 994 | return int64(cOut), true, nil 995 | } 996 | 997 | func (oa *OptionAccessor) GetFloat64Option(name string) (float64, bool, error) { 998 | return oa.GetFloat64OptionWithFlags(name, OptionSearchChildren) 999 | } 1000 | 1001 | func (oa *OptionAccessor) GetFloat64OptionWithFlags(name string, flags OptionSearchFlags) (float64, bool, error) { 1002 | var cOut C.double 1003 | cName := C.CString(name) 1004 | defer C.free(unsafe.Pointer(cName)) 1005 | searchFlags := oa.searchFlags(flags) 1006 | code := C.av_opt_get_double(oa.obj, cName, searchFlags, &cOut) 1007 | if code < 0 { 1008 | return 0, false, getOptionError(code) 1009 | } 1010 | return float64(cOut), true, nil 1011 | } 1012 | 1013 | func (oa *OptionAccessor) GetRationalOption(name string) (*Rational, error) { 1014 | return oa.GetRationalOptionWithFlags(name, OptionSearchChildren) 1015 | } 1016 | 1017 | func (oa *OptionAccessor) GetRationalOptionWithFlags(name string, flags OptionSearchFlags) (*Rational, error) { 1018 | cOut := &Rational{} 1019 | cName := C.CString(name) 1020 | defer C.free(unsafe.Pointer(cName)) 1021 | searchFlags := oa.searchFlags(flags) 1022 | code := C.av_opt_get_q(oa.obj, cName, searchFlags, &cOut.CAVRational) 1023 | if code < 0 { 1024 | return nil, getOptionError(code) 1025 | } 1026 | return cOut, nil 1027 | } 1028 | 1029 | func (oa *OptionAccessor) GetImageSizeOption(name string) (int, int, bool, error) { 1030 | return oa.GetImageSizeOptionWithFlags(name, OptionSearchChildren) 1031 | } 1032 | 1033 | func (oa *OptionAccessor) GetImageSizeOptionWithFlags(name string, flags OptionSearchFlags) (int, int, bool, error) { 1034 | var cOut1, cOut2 C.int 1035 | cName := C.CString(name) 1036 | defer C.free(unsafe.Pointer(cName)) 1037 | searchFlags := oa.searchFlags(flags) 1038 | code := C.av_opt_get_image_size(oa.obj, cName, searchFlags, &cOut1, &cOut2) 1039 | if code < 0 { 1040 | return 0, 0, false, getOptionError(code) 1041 | } 1042 | return int(cOut1), int(cOut2), true, nil 1043 | } 1044 | 1045 | func (oa *OptionAccessor) GetPixelFormatOption(name string) (PixelFormat, bool, error) { 1046 | return oa.GetPixelFormatOptionWithFlags(name, OptionSearchChildren) 1047 | } 1048 | 1049 | func (oa *OptionAccessor) GetPixelFormatOptionWithFlags(name string, flags OptionSearchFlags) (PixelFormat, bool, error) { 1050 | var cOut C.enum_AVPixelFormat 1051 | cName := C.CString(name) 1052 | defer C.free(unsafe.Pointer(cName)) 1053 | searchFlags := oa.searchFlags(flags) 1054 | code := C.av_opt_get_pixel_fmt(oa.obj, cName, searchFlags, &cOut) 1055 | if code < 0 { 1056 | return 0, false, getOptionError(code) 1057 | } 1058 | return PixelFormat(cOut), true, nil 1059 | } 1060 | 1061 | func (oa *OptionAccessor) GetSampleFormatOption(name string) (SampleFormat, bool, error) { 1062 | return oa.GetSampleFormatOptionWithFlags(name, OptionSearchChildren) 1063 | } 1064 | 1065 | func (oa *OptionAccessor) GetSampleFormatOptionWithFlags(name string, flags OptionSearchFlags) (SampleFormat, bool, error) { 1066 | var cOut C.enum_AVSampleFormat 1067 | cName := C.CString(name) 1068 | defer C.free(unsafe.Pointer(cName)) 1069 | searchFlags := oa.searchFlags(flags) 1070 | code := C.av_opt_get_sample_fmt(oa.obj, cName, searchFlags, &cOut) 1071 | if code < 0 { 1072 | return 0, false, getOptionError(code) 1073 | } 1074 | return SampleFormat(cOut), true, nil 1075 | } 1076 | 1077 | func (oa *OptionAccessor) GetVideoRateOption(name string) (*Rational, error) { 1078 | return oa.GetVideoRateOptionWithFlags(name, OptionSearchChildren) 1079 | } 1080 | 1081 | func (oa *OptionAccessor) GetVideoRateOptionWithFlags(name string, flags OptionSearchFlags) (*Rational, error) { 1082 | cOut := &Rational{} 1083 | cName := C.CString(name) 1084 | defer C.free(unsafe.Pointer(cName)) 1085 | searchFlags := oa.searchFlags(flags) 1086 | code := C.av_opt_get_video_rate(oa.obj, cName, searchFlags, &cOut.CAVRational) 1087 | if code < 0 { 1088 | return nil, getOptionError(code) 1089 | } 1090 | return cOut, nil 1091 | } 1092 | 1093 | func (oa *OptionAccessor) GetChannelLayoutOption(name string) (int64, bool, error) { 1094 | return oa.GetChannelLayoutOptionWithFlags(name, OptionSearchChildren) 1095 | } 1096 | 1097 | func (oa *OptionAccessor) GetChannelLayoutOptionWithFlags(name string, flags OptionSearchFlags) (int64, bool, error) { 1098 | var cOut C.int64_t 1099 | cName := C.CString(name) 1100 | defer C.free(unsafe.Pointer(cName)) 1101 | searchFlags := oa.searchFlags(flags) 1102 | code := C.av_opt_get_channel_layout(oa.obj, cName, searchFlags, &cOut) 1103 | if code < 0 { 1104 | return 0, false, getOptionError(code) 1105 | } 1106 | return int64(cOut), true, nil 1107 | } 1108 | 1109 | func (oa *OptionAccessor) GetDictionaryOption(name string) (*Dictionary, error) { 1110 | return oa.GetDictionaryOptionWithFlags(name, OptionSearchChildren) 1111 | } 1112 | 1113 | func (oa *OptionAccessor) GetDictionaryOptionWithFlags(name string, flags OptionSearchFlags) (*Dictionary, error) { 1114 | cOut := &Dictionary{} 1115 | cName := C.CString(name) 1116 | defer C.free(unsafe.Pointer(cName)) 1117 | searchFlags := oa.searchFlags(flags) 1118 | code := C.av_opt_get_dict_val(oa.obj, cName, searchFlags, cOut.pointer()) 1119 | if code < 0 { 1120 | return nil, getOptionError(code) 1121 | } 1122 | return cOut, nil 1123 | } 1124 | 1125 | func (oa *OptionAccessor) SetOption(name, value string) error { 1126 | return oa.SetOptionWithFlags(name, value, OptionSearchChildren) 1127 | } 1128 | 1129 | func (oa *OptionAccessor) SetOptionWithFlags(name, value string, flags OptionSearchFlags) error { 1130 | cName := C.CString(name) 1131 | defer C.free(unsafe.Pointer(cName)) 1132 | cValue := C.CString(value) 1133 | defer C.free(unsafe.Pointer(cValue)) 1134 | searchFlags := oa.searchFlags(flags) 1135 | code := C.av_opt_set(oa.obj, cName, cValue, searchFlags) 1136 | if code < 0 { 1137 | return NewErrorFromCode(ErrorCode(code)) 1138 | } 1139 | return nil 1140 | } 1141 | 1142 | func (oa *OptionAccessor) SetInt64Option(name string, value int64) error { 1143 | return oa.SetInt64OptionWithFlags(name, value, OptionSearchChildren) 1144 | } 1145 | 1146 | func (oa *OptionAccessor) SetInt64OptionWithFlags(name string, value int64, flags OptionSearchFlags) error { 1147 | cName := C.CString(name) 1148 | defer C.free(unsafe.Pointer(cName)) 1149 | searchFlags := oa.searchFlags(flags) 1150 | code := C.av_opt_set_int(oa.obj, cName, (C.int64_t)(value), searchFlags) 1151 | if code < 0 { 1152 | return NewErrorFromCode(ErrorCode(code)) 1153 | } 1154 | return nil 1155 | } 1156 | 1157 | func (oa *OptionAccessor) SetFloat64Option(name string, value float64) error { 1158 | return oa.SetFloat64OptionWithFlags(name, value, OptionSearchChildren) 1159 | } 1160 | 1161 | func (oa *OptionAccessor) SetFloat64OptionWithFlags(name string, value float64, flags OptionSearchFlags) error { 1162 | cName := C.CString(name) 1163 | defer C.free(unsafe.Pointer(cName)) 1164 | searchFlags := oa.searchFlags(flags) 1165 | code := C.av_opt_set_double(oa.obj, cName, (C.double)(value), searchFlags) 1166 | if code < 0 { 1167 | return NewErrorFromCode(ErrorCode(code)) 1168 | } 1169 | return nil 1170 | } 1171 | 1172 | func (oa *OptionAccessor) SetRationalOption(name string, value *Rational) error { 1173 | return oa.SetRationalOptionWithFlags(name, value, OptionSearchChildren) 1174 | } 1175 | 1176 | func (oa *OptionAccessor) SetRationalOptionWithFlags(name string, value *Rational, flags OptionSearchFlags) error { 1177 | cName := C.CString(name) 1178 | defer C.free(unsafe.Pointer(cName)) 1179 | searchFlags := oa.searchFlags(flags) 1180 | code := C.av_opt_set_q(oa.obj, cName, value.CAVRational, searchFlags) 1181 | if code < 0 { 1182 | return NewErrorFromCode(ErrorCode(code)) 1183 | } 1184 | return nil 1185 | } 1186 | 1187 | func (oa *OptionAccessor) SetBinaryOption(name string, value unsafe.Pointer, size int) error { 1188 | return oa.SetBinaryOptionWithFlags(name, value, size, OptionSearchChildren) 1189 | } 1190 | 1191 | func (oa *OptionAccessor) SetBinaryOptionWithFlags(name string, value unsafe.Pointer, size int, flags OptionSearchFlags) error { 1192 | cName := C.CString(name) 1193 | defer C.free(unsafe.Pointer(cName)) 1194 | searchFlags := oa.searchFlags(flags) 1195 | code := C.av_opt_set_bin(oa.obj, cName, (*C.uint8_t)(value), (C.int)(size), searchFlags) 1196 | if code < 0 { 1197 | return NewErrorFromCode(ErrorCode(code)) 1198 | } 1199 | return nil 1200 | } 1201 | 1202 | func (oa *OptionAccessor) SetIntArrayOption(name string, value []int) error { 1203 | return oa.SetIntArrayOptionWithFlags(name, value, OptionSearchChildren) 1204 | } 1205 | 1206 | func (oa *OptionAccessor) SetIntArrayOptionWithFlags(name string, value []int, flags OptionSearchFlags) error { 1207 | var ptr unsafe.Pointer 1208 | var value2 []C.int 1209 | num := len(value) 1210 | if num > 0 { 1211 | if C.sizeof_int == unsafe.Sizeof(value[0]) { 1212 | ptr = unsafe.Pointer(&value[0]) 1213 | } else { 1214 | value2 = make([]C.int, num, num) 1215 | for i := 0; i < num; i++ { 1216 | value2[i] = C.int(value[i]) 1217 | } 1218 | ptr = unsafe.Pointer(&value2[0]) 1219 | } 1220 | } 1221 | return oa.SetBinaryOptionWithFlags(name, ptr, num*C.sizeof_int, flags) 1222 | } 1223 | 1224 | func (oa *OptionAccessor) SetImageSizeOption(name string, width, height int) error { 1225 | return oa.SetImageSizeOptionWithFlags(name, width, height, OptionSearchChildren) 1226 | } 1227 | 1228 | func (oa *OptionAccessor) SetImageSizeOptionWithFlags(name string, width, height int, flags OptionSearchFlags) error { 1229 | cName := C.CString(name) 1230 | defer C.free(unsafe.Pointer(cName)) 1231 | searchFlags := oa.searchFlags(flags) 1232 | code := C.av_opt_set_image_size(oa.obj, cName, (C.int)(width), (C.int)(height), searchFlags) 1233 | if code < 0 { 1234 | return NewErrorFromCode(ErrorCode(code)) 1235 | } 1236 | return nil 1237 | } 1238 | 1239 | func (oa *OptionAccessor) SetPixelFormatOption(name string, value PixelFormat) error { 1240 | return oa.SetPixelFormatOptionWithFlags(name, value, OptionSearchChildren) 1241 | } 1242 | 1243 | func (oa *OptionAccessor) SetPixelFormatOptionWithFlags(name string, value PixelFormat, flags OptionSearchFlags) error { 1244 | cName := C.CString(name) 1245 | defer C.free(unsafe.Pointer(cName)) 1246 | searchFlags := oa.searchFlags(flags) 1247 | code := C.av_opt_set_pixel_fmt(oa.obj, cName, (C.enum_AVPixelFormat)(value), searchFlags) 1248 | if code < 0 { 1249 | return NewErrorFromCode(ErrorCode(code)) 1250 | } 1251 | return nil 1252 | } 1253 | 1254 | func (oa *OptionAccessor) SetVideoRateOption(name string, value *Rational) error { 1255 | return oa.SetVideoRateOptionWithFlags(name, value, OptionSearchChildren) 1256 | } 1257 | 1258 | func (oa *OptionAccessor) SetVideoRateOptionWithFlags(name string, value *Rational, flags OptionSearchFlags) error { 1259 | cName := C.CString(name) 1260 | defer C.free(unsafe.Pointer(cName)) 1261 | searchFlags := oa.searchFlags(flags) 1262 | code := C.av_opt_set_video_rate(oa.obj, cName, value.CAVRational, searchFlags) 1263 | if code < 0 { 1264 | return NewErrorFromCode(ErrorCode(code)) 1265 | } 1266 | return nil 1267 | } 1268 | 1269 | func (oa *OptionAccessor) SetChannelLayoutOption(name string, value int64) error { 1270 | return oa.SetChannelLayoutOptionWithFlags(name, value, OptionSearchChildren) 1271 | } 1272 | 1273 | func (oa *OptionAccessor) SetChannelLayoutOptionWithFlags(name string, value int64, flags OptionSearchFlags) error { 1274 | cName := C.CString(name) 1275 | defer C.free(unsafe.Pointer(cName)) 1276 | searchFlags := oa.searchFlags(flags) 1277 | code := C.av_opt_set_channel_layout(oa.obj, cName, (C.int64_t)(value), searchFlags) 1278 | if code < 0 { 1279 | return NewErrorFromCode(ErrorCode(code)) 1280 | } 1281 | return nil 1282 | } 1283 | 1284 | func (oa *OptionAccessor) SetDictionaryOption(name string, value *Dictionary) error { 1285 | return oa.SetDictionaryOptionWithFlags(name, value, OptionSearchChildren) 1286 | } 1287 | 1288 | func (oa *OptionAccessor) SetDictionaryOptionWithFlags(name string, value *Dictionary, flags OptionSearchFlags) error { 1289 | cName := C.CString(name) 1290 | defer C.free(unsafe.Pointer(cName)) 1291 | searchFlags := oa.searchFlags(flags) 1292 | code := C.av_opt_set_dict_val(oa.obj, cName, value.value(), searchFlags) 1293 | if code < 0 { 1294 | return NewErrorFromCode(ErrorCode(code)) 1295 | } 1296 | return nil 1297 | } 1298 | 1299 | func (oa *OptionAccessor) searchFlags(flags OptionSearchFlags) C.int { 1300 | flags &^= OptionSearchFakeObj 1301 | if oa.fake { 1302 | flags |= OptionSearchFakeObj 1303 | } 1304 | return C.int(flags) 1305 | } 1306 | 1307 | func getOptionError(code C.int) error { 1308 | if ErrorCode(code) == ErrorCodeOptionNotFound { 1309 | return nil 1310 | } 1311 | return NewErrorFromCode(ErrorCode(code)) 1312 | } 1313 | 1314 | type Expr struct { 1315 | CAVExpr *C.struct_AVExpr 1316 | } 1317 | 1318 | func NewExpr(value string, constNames []string) (*Expr, error) { 1319 | e := NewExprFromC(nil) 1320 | cValue := C.CString(value) 1321 | defer C.free(unsafe.Pointer(cValue)) 1322 | cConstNames := make([]*C.char, C.int(len(constNames)+1)) 1323 | for i, constName := range constNames { 1324 | cConstNames[i] = C.CString(constName) 1325 | defer C.free(unsafe.Pointer(cConstNames[i])) 1326 | } 1327 | code := C.go_av_expr_parse2(&e.CAVExpr, cValue, (**C.char)(&cConstNames[0]), 0, nil) 1328 | if code < 0 { 1329 | return nil, NewErrorFromCode(ErrorCode(code)) 1330 | } 1331 | return e, nil 1332 | } 1333 | 1334 | func NewExprFromC(cExpr unsafe.Pointer) *Expr { 1335 | return &Expr{CAVExpr: (*C.struct_AVExpr)(cExpr)} 1336 | } 1337 | 1338 | func (e *Expr) Evaluate(constValues []float64) (float64, error) { 1339 | if len(constValues) == 0 { 1340 | return 0, ErrInvalidArgumentSize 1341 | } 1342 | var cRet C.double 1343 | cRet = C.av_expr_eval(e.CAVExpr, (*C.double)(&constValues[0]), nil) 1344 | return float64(cRet), nil 1345 | } 1346 | 1347 | func (e *Expr) Free() { 1348 | if e.CAVExpr != nil { 1349 | defer C.av_expr_free(e.CAVExpr) 1350 | e.CAVExpr = nil 1351 | } 1352 | } 1353 | 1354 | func String(str string) *string { 1355 | return &str 1356 | } 1357 | 1358 | func Rescale(a, b, c int64) int64 { 1359 | return int64(C.av_rescale(C.int64_t(a), C.int64_t(b), C.int64_t(c))) 1360 | } 1361 | 1362 | func RescaleByRationals(a int64, bq, cq *Rational) int64 { 1363 | return int64(C.av_rescale_q(C.int64_t(a), bq.CAVRational, cq.CAVRational)) 1364 | } 1365 | 1366 | func ParseRational(ratio string, max int) (*Rational, error) { 1367 | cRatio := C.CString(ratio) 1368 | defer C.free(unsafe.Pointer(cRatio)) 1369 | var cRational C.AVRational 1370 | code := C.av_parse_ratio(&cRational, cRatio, C.int(max), C.int(0), nil) 1371 | if code < 0 { 1372 | return nil, NewErrorFromCode(ErrorCode(code)) 1373 | } 1374 | return NewRationalFromC(unsafe.Pointer(&cRational)), nil 1375 | } 1376 | 1377 | func ParseTime(timestr string, duration bool) (int64, error) { 1378 | cTimestr := C.CString(timestr) 1379 | defer C.free(unsafe.Pointer(cTimestr)) 1380 | x := C.int64_t(0) 1381 | code := C.av_parse_time(&x, cTimestr, boolToCInt(duration)) 1382 | if code < 0 { 1383 | return 0, NewErrorFromCode(ErrorCode(code)) 1384 | } 1385 | return int64(x), nil 1386 | } 1387 | 1388 | func Clip(x, min, max int) int { 1389 | return int(C.av_clip(C.int(x), C.int(min), C.int(max))) 1390 | } 1391 | 1392 | func boolToCInt(b bool) C.int { 1393 | if b { 1394 | return 1 1395 | } 1396 | return 0 1397 | } 1398 | -------------------------------------------------------------------------------- /avutil/avutil_test.go: -------------------------------------------------------------------------------- 1 | package avutil 2 | 3 | import ( 4 | "os" 5 | "reflect" 6 | "strings" 7 | "syscall" 8 | "testing" 9 | 10 | "github.com/shirou/gopsutil/process" 11 | ) 12 | 13 | func TestVersion(t *testing.T) { 14 | major, minor, micro := Version() 15 | if major < 55 || minor < 0 || micro < 0 { 16 | t.Fatalf("Invalid version") 17 | } 18 | } 19 | 20 | func TestNewErrorFromCode(t *testing.T) { 21 | err := NewErrorFromCode(0) 22 | if err == nil { 23 | t.Fatalf("Expecting error") 24 | } 25 | } 26 | 27 | func TestErrorFromCodeError(t *testing.T) { 28 | err := NewErrorFromCode(-1) 29 | if err.Error() != "Operation not permitted" { 30 | t.Fatal(err) 31 | } 32 | } 33 | 34 | func TestErrorFromCodeCode(t *testing.T) { 35 | err := NewErrorFromCode(-2) 36 | if err.Code() != -2 { 37 | t.Fatal(err) 38 | } 39 | } 40 | 41 | func TestErrorFromErrnoError(t *testing.T) { 42 | err := NewErrorFromCode(ErrnoErrorCode(syscall.EPERM)) 43 | if err.Error() != "Operation not permitted" { 44 | t.Fatal(err) 45 | } 46 | err = NewErrorFromCode(ErrnoErrorCode(syscall.ENOSYS)) 47 | if err.Error() != "Function not implemented" { 48 | t.Fatal(err) 49 | } 50 | } 51 | 52 | func TestNewDictionary(t *testing.T) { 53 | dict := NewDictionary() 54 | if dict == nil { 55 | t.Fatalf("Expecting dictionary") 56 | } 57 | defer dict.Free() 58 | if count := dict.Count(); count != 0 { 59 | t.Fatalf("Expecting count but got %d", count) 60 | } 61 | } 62 | 63 | func TestDictionarySetDeleteCount(t *testing.T) { 64 | dict := NewDictionary() 65 | defer dict.Free() 66 | if err := dict.Set("foo", "bar"); err != nil { 67 | t.Fatal(err) 68 | } 69 | if count := dict.Count(); count != 1 { 70 | t.Fatalf("Expecting count but got %d", count) 71 | } 72 | if err := dict.Set("", ""); err != nil { 73 | t.Fatal(err) 74 | } 75 | if count := dict.Count(); count != 2 { 76 | t.Fatalf("Expecting count but got %d", count) 77 | } 78 | if err := dict.Delete("foo"); err != nil { 79 | t.Fatal(err) 80 | } 81 | if count := dict.Count(); count != 1 { 82 | t.Fatalf("Expecting count but got %d", count) 83 | } 84 | } 85 | 86 | func TestDictionarySetGetDeleteHas(t *testing.T) { 87 | dict := NewDictionary() 88 | defer dict.Free() 89 | if value, ok := dict.GetOk("foo"); ok || value != "" { 90 | t.Fatal("Not expecting value") 91 | } 92 | if dict.Has("foo") { 93 | t.Fatal("Not expecting key") 94 | } 95 | dict.Set("foo", "bar") 96 | if value, ok := dict.GetOk("foo"); !ok || value != "bar" { 97 | t.Fatal("Expecting value") 98 | } 99 | if !dict.Has("foo") { 100 | t.Fatal("Epecting key") 101 | } 102 | if value, ok := dict.GetOk(""); ok || value != "" { 103 | t.Fatal("Not expecting value") 104 | } 105 | if dict.Has("") { 106 | t.Fatal("Not expecting value") 107 | } 108 | dict.Set("", "") 109 | if value, ok := dict.GetOk(""); !ok || value != "" { 110 | t.Fatal("Not expecting value") 111 | } 112 | if !dict.Has("") { 113 | t.Fatal("Epecting key") 114 | } 115 | dict.Delete("foo") 116 | if value, ok := dict.GetOk("foo"); ok || value != "" { 117 | t.Fatal("Not expecting value") 118 | } 119 | if dict.Has("foo") { 120 | t.Fatal("Not expecting key") 121 | } 122 | } 123 | 124 | func TestDictionarySetOverwrite(t *testing.T) { 125 | dict := NewDictionary() 126 | defer dict.Free() 127 | if err := dict.Set("foo", "bar"); err != nil { 128 | t.Fatal(err) 129 | } 130 | if count := dict.Count(); count != 1 { 131 | t.Fatalf("Expecting count but got %d", count) 132 | } 133 | if value, ok := dict.GetOk("foo"); !ok || value != "bar" { 134 | t.Fatal("Expecting value") 135 | } 136 | if err := dict.Set("foo", "BAR"); err != nil { 137 | t.Fatal(err) 138 | } 139 | if count := dict.Count(); count != 1 { 140 | t.Fatalf("Expecting count but got %d", count) 141 | } 142 | if value, ok := dict.GetOk("foo"); !ok || value != "BAR" { 143 | t.Fatal("Expecting value") 144 | } 145 | } 146 | 147 | func TestDictionarySetKeys(t *testing.T) { 148 | dict := NewDictionary() 149 | defer dict.Free() 150 | keys := dict.Keys() 151 | if keys != nil { 152 | t.Fatalf("Expecting no keys but got %v", keys) 153 | } 154 | if err := dict.Set("foo", "bar"); err != nil { 155 | t.Fatal(err) 156 | } 157 | keys = dict.Keys() 158 | if !reflect.DeepEqual(keys, []string{"foo"}) { 159 | t.Fatalf("Expecting keys but got %v", keys) 160 | } 161 | if err := dict.Set("", ""); err != nil { 162 | t.Fatal(err) 163 | } 164 | keys = dict.Keys() 165 | if !reflect.DeepEqual(keys, []string{"foo", ""}) { 166 | t.Fatalf("Expecting keys but got %v", keys) 167 | } 168 | if err := dict.Set("bar", "foo"); err != nil { 169 | t.Fatal(err) 170 | } 171 | keys = dict.Keys() 172 | if !reflect.DeepEqual(keys, []string{"foo", "", "bar"}) { 173 | t.Fatalf("Expecting keys but got %v", keys) 174 | } 175 | } 176 | 177 | func TestDictionarySetValues(t *testing.T) { 178 | dict := NewDictionary() 179 | defer dict.Free() 180 | values := dict.Values() 181 | if values != nil { 182 | t.Fatalf("Expecting no values but got %v", values) 183 | } 184 | if err := dict.Set("foo", "bar"); err != nil { 185 | t.Fatal(err) 186 | } 187 | values = dict.Values() 188 | if !reflect.DeepEqual(values, []string{"bar"}) { 189 | t.Fatalf("Expecting values but got %v", values) 190 | } 191 | if err := dict.Set("", ""); err != nil { 192 | t.Fatal(err) 193 | } 194 | values = dict.Values() 195 | if !reflect.DeepEqual(values, []string{"bar", ""}) { 196 | t.Fatalf("Expecting values but got %v", values) 197 | } 198 | if err := dict.Set("bar", "foo"); err != nil { 199 | t.Fatal(err) 200 | } 201 | values = dict.Values() 202 | if !reflect.DeepEqual(values, []string{"bar", "", "foo"}) { 203 | t.Fatalf("Expecting values but got %v", values) 204 | } 205 | } 206 | 207 | func TestDictionarySetMap(t *testing.T) { 208 | dict := NewDictionary() 209 | defer dict.Free() 210 | m := dict.Map() 211 | if m != nil { 212 | t.Fatalf("Expecting no map but got %v", m) 213 | } 214 | if err := dict.Set("foo", "bar"); err != nil { 215 | t.Fatal(err) 216 | } 217 | m = dict.Map() 218 | if !reflect.DeepEqual(m, map[string]string{"foo": "bar"}) { 219 | t.Fatalf("Expecting map but got %v", m) 220 | } 221 | if err := dict.Set("", ""); err != nil { 222 | t.Fatal(err) 223 | } 224 | m = dict.Map() 225 | if !reflect.DeepEqual(m, map[string]string{"foo": "bar", "": ""}) { 226 | t.Fatalf("Expecting map but got %v", m) 227 | } 228 | if err := dict.Set("bar", "foo"); err != nil { 229 | t.Fatal(err) 230 | } 231 | m = dict.Map() 232 | if !reflect.DeepEqual(m, map[string]string{"foo": "bar", "": "", "bar": "foo"}) { 233 | t.Fatalf("Expecting map but got %v", m) 234 | } 235 | } 236 | 237 | func TestDictionaryMatchCase(t *testing.T) { 238 | dict := NewDictionary() 239 | defer dict.Free() 240 | if err := dict.Set("foo", "bar"); err != nil { 241 | t.Fatal(err) 242 | } 243 | if count := dict.Count(); count != 1 { 244 | t.Fatalf("Expecting count but got %d", count) 245 | } 246 | if err := dict.Set("foo", "FOO"); err != nil { 247 | t.Fatal(err) 248 | } 249 | if count := dict.Count(); count != 1 { 250 | t.Fatalf("Expecting count but got %d", count) 251 | } 252 | if value, ok := dict.GetOk("fOo"); ok || value != "" { 253 | t.Fatal("Not expecting value") 254 | } 255 | if dict.Has("fOo") { 256 | t.Fatal("Not expecting value") 257 | } 258 | if value, ok := dict.GetInsensitiveOk("fOo"); !ok || value != "FOO" { 259 | t.Fatal("Expecting value") 260 | } 261 | if !dict.HasInsensitive("fOo") { 262 | t.Fatal("Expecting value") 263 | } 264 | if err := dict.SetInsensitive("FOo", "FOOBAR"); err != nil { 265 | t.Fatal(err) 266 | } 267 | if count := dict.Count(); count != 1 { 268 | t.Fatalf("Expecting count but got %d", count) 269 | } 270 | if value, ok := dict.GetOk("fOo"); ok || value != "" { 271 | t.Fatal("Not expecting value") 272 | } 273 | if dict.Has("fOo") { 274 | t.Fatal("Not expecting value") 275 | } 276 | if value, ok := dict.GetInsensitiveOk("fOo"); !ok || value != "FOOBAR" { 277 | t.Fatal("Expecting value") 278 | } 279 | if !dict.HasInsensitive("fOo") { 280 | t.Fatal("Expecting value") 281 | } 282 | if err := dict.Set("fOo", "BAR"); err != nil { 283 | t.Fatal(err) 284 | } 285 | if count := dict.Count(); count != 2 { 286 | t.Fatalf("Expecting count but got %d", count) 287 | } 288 | m := dict.Map() 289 | if !reflect.DeepEqual(m, map[string]string{"FOo": "FOOBAR", "fOo": "BAR"}) { 290 | t.Fatalf("Expecting map but got %v", m) 291 | } 292 | } 293 | 294 | func TestDictionaryFreeCountFreeCount(t *testing.T) { 295 | dict := NewDictionary() 296 | dict.Free() 297 | if count := dict.Count(); count != 0 { 298 | t.Fatalf("Expecting count but got %d", count) 299 | } 300 | dict.Free() 301 | if count := dict.Count(); count != 0 { 302 | t.Fatalf("Expecting count but got %d", count) 303 | } 304 | } 305 | 306 | func TestDictionaryFreeSetGetFreeGet(t *testing.T) { 307 | dict := NewDictionary() 308 | dict.Free() 309 | if dict.CAVDictionary != nil || dict.pCAVDictionary != nil { 310 | t.Fatal("Invalid pointer") 311 | } 312 | if err := dict.Set("foo", "bar"); err != nil { 313 | t.Fatal(err) 314 | } 315 | if dict.CAVDictionary != nil || dict.pCAVDictionary == nil { 316 | t.Fatal("Invalid pointer") 317 | } 318 | if value, ok := dict.GetOk("foo"); !ok || value != "bar" { 319 | t.Fatal("Expecting value") 320 | } 321 | dict.Free() 322 | if dict.CAVDictionary != nil || dict.pCAVDictionary != nil { 323 | t.Fatal("Invalid pointer") 324 | } 325 | if value, ok := dict.GetOk("foo"); ok || value != "" { 326 | t.Fatal("Not expecting value") 327 | } 328 | } 329 | 330 | func TestDictionaryCopyEmpty(t *testing.T) { 331 | dict := NewDictionary() 332 | defer dict.Free() 333 | 334 | dict2 := dict.Copy() 335 | defer dict2.Free() 336 | 337 | if count := dict.Count(); count != 0 { 338 | t.Fatalf("Expecting count but got %d", count) 339 | } 340 | 341 | if count := dict2.Count(); count != 0 { 342 | t.Fatalf("Expecting count but got %d", count) 343 | } 344 | } 345 | 346 | func TestDictionaryCopyNonEmpty(t *testing.T) { 347 | dict := NewDictionary() 348 | defer dict.Free() 349 | if err := dict.Set("foo", "bar"); err != nil { 350 | t.Fatal(err) 351 | } 352 | if err := dict.Set("FOO", "BAR"); err != nil { 353 | t.Fatal(err) 354 | } 355 | 356 | dict2 := dict.Copy() 357 | defer dict2.Free() 358 | 359 | m := dict.Map() 360 | if !reflect.DeepEqual(m, map[string]string{"foo": "bar", "FOO": "BAR"}) { 361 | t.Fatalf("Expecting map but got %v", m) 362 | } 363 | 364 | m = dict2.Map() 365 | if !reflect.DeepEqual(m, map[string]string{"foo": "bar", "FOO": "BAR"}) { 366 | t.Fatalf("Expecting map but got %v", m) 367 | } 368 | 369 | if err := dict.Set("foo", "DICT"); err != nil { 370 | t.Fatal(err) 371 | } 372 | 373 | if err := dict2.Set("foo", "DICT2"); err != nil { 374 | t.Fatal(err) 375 | } 376 | 377 | m = dict.Map() 378 | if !reflect.DeepEqual(m, map[string]string{"foo": "DICT", "FOO": "BAR"}) { 379 | t.Fatalf("Expecting map but got %v", m) 380 | } 381 | 382 | m = dict2.Map() 383 | if !reflect.DeepEqual(m, map[string]string{"foo": "DICT2", "FOO": "BAR"}) { 384 | t.Fatalf("Expecting map but got %v", m) 385 | } 386 | } 387 | 388 | func TestDictionaryNewSetFreeLeak10M(t *testing.T) { 389 | before := testMemoryUsed(t) 390 | for i := 0; i < 10000000; i++ { 391 | dict := NewDictionary() 392 | if err := dict.Set("test", "value"); err != nil { 393 | t.Fatal(err) 394 | } 395 | dict.Free() 396 | } 397 | testMemoryLeak(t, before, 50*1024*1024) 398 | } 399 | 400 | type dictionaryStringTestData struct { 401 | contents map[string]string 402 | keyValSep byte 403 | pairsSep byte 404 | 405 | expected string 406 | err string 407 | } 408 | 409 | func TestDictionaryString(t *testing.T) { 410 | datas := []*dictionaryStringTestData{ 411 | { 412 | contents: map[string]string{}, 413 | keyValSep: '=', 414 | pairsSep: ':', 415 | expected: "", 416 | }, 417 | { 418 | contents: map[string]string{ 419 | "key1": "val1", 420 | "key2": "val2", 421 | }, 422 | keyValSep: '=', 423 | pairsSep: ':', 424 | expected: "key1=val1:key2=val2", 425 | }, 426 | { 427 | contents: map[string]string{}, 428 | keyValSep: '\\', 429 | pairsSep: ':', 430 | err: "Invalid argument", 431 | }, 432 | } 433 | 434 | for i, data := range datas { 435 | dict := NewDictionary() 436 | for k, v := range data.contents { 437 | if err := dict.Set(k, v); err != nil { 438 | dict.Free() 439 | t.Fatal(err) 440 | } 441 | } 442 | 443 | result, err := dict.String(data.keyValSep, data.pairsSep) 444 | if err != nil && err.Error() != data.err { 445 | dict.Free() 446 | t.Fatalf("[TestDictionaryString - case%d] got err=%s, expected err=%s", i+1, err.Error(), data.err) 447 | } 448 | if len(result) > 0 { 449 | rows := strings.Split(result, string(data.pairsSep)) 450 | if len(rows) != len(data.contents) { 451 | dict.Free() 452 | t.Fatalf("[TestDictionaryString - case%d] got result=%s, expected result=%s, ", i+1, result, data.expected) 453 | } 454 | for _, row := range rows { 455 | keyVal := strings.Split(row, string(data.keyValSep)) 456 | if len(keyVal) != 2 { 457 | dict.Free() 458 | t.Fatalf("[TestDictionaryString - case%d] got result=%s, expected result=%s, ", i+1, result, data.expected) 459 | } 460 | if keyVal[1] != data.contents[keyVal[0]] { 461 | dict.Free() 462 | t.Fatalf("[TestDictionaryString - case%d] got result=%s, expected result=%s, ", i+1, result, data.expected) 463 | } 464 | } 465 | } else if len(data.expected) != 0 { 466 | dict.Free() 467 | t.Fatalf("[TestDictionaryString - case%d] got result=%s, expected result=%s, ", i+1, result, data.expected) 468 | } 469 | dict.Free() 470 | } 471 | } 472 | 473 | func TestDictionaryStringLeak10M(t *testing.T) { 474 | dict := NewDictionary() 475 | defer dict.Free() 476 | if err := dict.Set("key1", "val1"); err != nil { 477 | t.Fatal(err) 478 | } 479 | if err := dict.Set("key2", "val2"); err != nil { 480 | t.Fatal(err) 481 | } 482 | before := testMemoryUsed(t) 483 | for i := 0; i < 10000000; i++ { 484 | if _, err := dict.String(':', '='); err != nil { 485 | t.Fatal(err) 486 | } 487 | } 488 | testMemoryLeak(t, before, 10*1024*1024) 489 | } 490 | 491 | func TestChannelLayouts(t *testing.T) { 492 | layouts := ChannelLayouts() 493 | if len(layouts) == 0 { 494 | t.Fatalf("Expecting channel layouts") 495 | } 496 | } 497 | 498 | type parseTimeTestData struct { 499 | timestr string 500 | duration bool 501 | expected int64 502 | } 503 | 504 | func TestParseTime(t *testing.T) { 505 | datas := []*parseTimeTestData{ 506 | { 507 | timestr: "1.5", 508 | duration: true, 509 | expected: 1500000, 510 | }, 511 | { 512 | timestr: "-1.5", 513 | duration: true, 514 | expected: -1500000, 515 | }, 516 | { 517 | timestr: "01:30", 518 | duration: true, 519 | expected: 90000000, 520 | }, 521 | { 522 | timestr: "01:01:30", 523 | duration: true, 524 | expected: 3690000000, 525 | }, 526 | { 527 | timestr: "2000-01-01 00:00:00Z", 528 | duration: false, 529 | expected: 946684800000000, 530 | }, 531 | } 532 | 533 | for _, data := range datas { 534 | result, err := ParseTime(data.timestr, data.duration) 535 | if err != nil { 536 | t.Fatal(err) 537 | } 538 | if result != data.expected { 539 | t.Fatalf("[TestParseTime] result=%d, NG, expected=%d", result, data.expected) 540 | } 541 | } 542 | } 543 | 544 | func TestFindPixelFormatByName(t *testing.T) { 545 | fmt, ok := FindPixelFormatByName("yuv420p") 546 | if !ok || fmt == PixelFormatNone { 547 | t.Errorf("Expecting pixel format") 548 | } 549 | fmt, ok = FindPixelFormatByName("invalid") 550 | if ok || fmt != PixelFormatNone { 551 | t.Errorf("Not expecting pixel format") 552 | } 553 | } 554 | 555 | func TestNewFrame(t *testing.T) { 556 | frame, err := NewFrame() 557 | if err != nil { 558 | t.Fatal(err) 559 | } 560 | if frame == nil { 561 | t.Fatalf("Expecting frame") 562 | } 563 | defer frame.Free() 564 | } 565 | 566 | func TestFramePacketDurationOK(t *testing.T) { 567 | frame, _ := NewFrame() 568 | defer frame.Free() 569 | result := frame.PacketDuration() 570 | if result != 0 { 571 | t.Fatalf("[TestFramePacketDurationOK] result=%d, NG expected=%d", result, 0) 572 | } 573 | } 574 | 575 | func TestFrameGetBuffer(t *testing.T) { 576 | frame, _ := NewFrame() 577 | defer frame.Free() 578 | if frame.Data(0) != nil { 579 | t.Fatalf("Expecting no data") 580 | } 581 | frame.SetWidth(32) 582 | frame.SetHeight(32) 583 | fmt, _ := FindPixelFormatByName("yuv420p") 584 | frame.SetPixelFormat(fmt) 585 | err := frame.GetBuffer() 586 | if err != nil { 587 | t.Fatal(err) 588 | } 589 | if frame.Data(0) == nil { 590 | t.Fatalf("Expecting data") 591 | } 592 | } 593 | 594 | func TestFrameNewFreeLeak10M(t *testing.T) { 595 | for i := 0; i < 10000000; i++ { 596 | frame, err := NewFrame() 597 | if err != nil { 598 | t.Fatal(err) 599 | } 600 | frame.Free() 601 | } 602 | } 603 | 604 | func TestFrameMetadata(t *testing.T) { 605 | frame, _ := NewFrame() 606 | defer frame.Free() 607 | 608 | result := frame.Metadata() 609 | if result != nil { 610 | t.Fatalf("[TestFrameMetadata] result=%v, NG expected nil", result) 611 | } 612 | 613 | dict := NewDictionary() 614 | defer dict.Free() 615 | if err := dict.Set("key", "value"); err != nil { 616 | t.Fatal(err) 617 | } 618 | frame.SetMetadata(dict) 619 | result = frame.Metadata() 620 | if !reflect.DeepEqual(result.Map(), dict.Map()) { 621 | t.Fatalf("[TestFrameMetadata] result=%v, NG expected=%v", result, dict) 622 | } 623 | 624 | frame.SetMetadata(nil) 625 | result = frame.Metadata() 626 | if result != nil { 627 | t.Fatalf("[TestFrameMetadata] result=%v, NG expected nil", result) 628 | } 629 | } 630 | 631 | func TestExprOK(t *testing.T) { 632 | expr := testExpr(t) 633 | defer expr.Free() 634 | } 635 | 636 | func TestExprOK100K(t *testing.T) { 637 | var exprs []*Expr 638 | defer func() { 639 | for _, expr := range exprs { 640 | defer expr.Free() 641 | } 642 | }() 643 | for i := 0; i < 100000; i++ { 644 | expr := testExpr(t) 645 | exprs = append(exprs, expr) 646 | } 647 | } 648 | 649 | func TestExprInvalidParams(t *testing.T) { 650 | type exprTestData struct { 651 | value string 652 | constNames []string 653 | } 654 | datas := []*exprTestData{ 655 | { 656 | value: "invalid", 657 | constNames: []string{"n", "n_forced", "prev_forced_n", "prev_forced_t", "t", ""}, 658 | }, 659 | { 660 | value: "gte(t,n_forced*5)", 661 | constNames: []string{"invalid"}, 662 | }, 663 | { 664 | value: "gte(t,n_forced*5)", 665 | constNames: []string{}, 666 | }, 667 | { 668 | value: "gte(t,n_forced*5)", 669 | constNames: nil, 670 | }, 671 | } 672 | for _, data := range datas { 673 | expr, err := NewExpr(data.value, data.constNames) 674 | if err == nil || err.Error() != "Invalid argument" { 675 | t.Fatalf("[TestExprInvalidParams] expected error but got %v", err) 676 | } 677 | if expr != nil { 678 | t.Fatal("[TestExprInvalidParams] expected nil, got expr.") 679 | expr.Free() 680 | } 681 | } 682 | } 683 | 684 | func TestExprEvaluateOK(t *testing.T) { 685 | expr := testExpr(t) 686 | defer expr.Free() 687 | constValues := []float64{0, 0, 0, 0, 0, 0} 688 | for i := 0; i <= 5; i++ { 689 | result, err := expr.Evaluate(constValues) 690 | if err != nil { 691 | t.Fatal(err) 692 | } 693 | if i == 0 || i == 5 { 694 | if result != 1 { 695 | t.Fatalf("[TestExprOK] result got: %f, expected: 1", result) 696 | } 697 | } else { 698 | if result != 0 { 699 | t.Fatalf("[TestExprOK] result got: %f, expected: 0", result) 700 | } 701 | } 702 | constValues[4] = float64(i) + 1 703 | if result > 0 { 704 | constValues[1]++ 705 | } 706 | } 707 | } 708 | 709 | func TestExprEvaluateInvalidParams(t *testing.T) { 710 | expr := testExpr(t) 711 | defer expr.Free() 712 | constValues := []float64{} 713 | result, err := expr.Evaluate(constValues) 714 | if err == nil { 715 | t.Fatal("[TestExprEvaluateInvalidParams] expected error.") 716 | } 717 | if result == 1 { 718 | t.Fatalf("[TestExprEvaluateInvalidParams] result got: %f, expected: 0", result) 719 | } 720 | } 721 | 722 | func testExpr(t *testing.T) *Expr { 723 | exprValue := "gte(t,n_forced*5)" 724 | constNames := []string{"n", "n_forced", "prev_forced_n", "prev_forced_t", "t", ""} 725 | expr, err := NewExpr(exprValue, constNames) 726 | if err != nil { 727 | t.Fatal(err) 728 | } 729 | if expr == nil { 730 | t.Fatal("[testExpr] expected expr, got null") 731 | } 732 | return expr 733 | } 734 | 735 | func TestClipOK(t *testing.T) { 736 | min := 1 737 | max := 4 738 | for x := min - 1; x <= max+1; x++ { 739 | result := Clip(x, min, max) 740 | if x < min { 741 | if result != min { 742 | t.Fatalf("[TestClipOK] result=%d, NG expected=%d", result, min) 743 | } 744 | } else if x > max { 745 | if result != max { 746 | t.Fatalf("[TestClipOK] result=%d, NG expected=%d", result, max) 747 | } 748 | } else { 749 | if result != x { 750 | t.Fatalf("[TestClipOK] result=%d, NG expected=%d", result, x) 751 | } 752 | } 753 | } 754 | } 755 | 756 | func TestString(t *testing.T) { 757 | expected := "test" 758 | result := String(expected) 759 | if result == nil { 760 | t.Fatalf("[TestString] result=nil, NG expected not nil") 761 | } 762 | if *result != expected { 763 | t.Fatalf("[TestString] result=%s, NG expected=%s", *result, expected) 764 | } 765 | } 766 | 767 | func TestParseRational(t *testing.T) { 768 | result, err := ParseRational("", 255) 769 | if err == nil { 770 | t.Fatalf("[TestParseRational] err=nil, NG expected error") 771 | } 772 | 773 | result, err = ParseRational("16:9", 20) 774 | if err != nil { 775 | t.Fatalf("[TestParseRational] err=%v, NG expected not error", err) 776 | } 777 | if result.Numerator() != 16 || result.Denominator() != 9 { 778 | t.Fatalf("[TestParseRational] result=%s, NG expected=%d:%d", result, 16, 9) 779 | } 780 | 781 | result, err = ParseRational("1.778", 255) 782 | if err != nil { 783 | t.Fatalf("[TestParseRational] err=%v, NG expected not error", err) 784 | } 785 | if result.Numerator() != 16 || result.Denominator() != 9 { 786 | t.Fatalf("[TestParseRational] result=%s, NG expected=%d/%d", result, 16, 9) 787 | } 788 | 789 | result, err = ParseRational("1.778", 500) 790 | if err != nil { 791 | t.Fatalf("[TestParseRational] err=%v, NG expected not error", err) 792 | } 793 | if result.Numerator() != 489 || result.Denominator() != 275 { 794 | t.Fatalf("[TestParseRational] result=%s, NG expected=%d/%d", result, 489, 275) 795 | } 796 | } 797 | 798 | func testMemoryUsed(t *testing.T) uint64 { 799 | p, err := process.NewProcess(int32(os.Getpid())) 800 | if err != nil { 801 | t.Fatal(err) 802 | } 803 | info, err := p.MemoryInfo() 804 | if err != nil { 805 | t.Fatal(err) 806 | } 807 | return info.RSS 808 | } 809 | 810 | func testMemoryLeak(t *testing.T, before uint64, diff uint64) { 811 | after := testMemoryUsed(t) 812 | if after > before && after-before > diff { 813 | t.Fatalf("memory leak detected: %d bytes", after-before) 814 | } 815 | } 816 | 817 | func TestFindPixelFormatDescriptorByPixelFormat(t *testing.T) { 818 | pixFmt, ok := FindPixelFormatByName("rgb48be") 819 | if !ok { 820 | t.Fatalf("pixel format not found") 821 | } 822 | descriptor := FindPixelFormatDescriptorByPixelFormat(pixFmt) 823 | if descriptor == nil { 824 | t.Fatalf("[TestFindPixelFormatDescriptorByPixelFormat] descriptor is nil, NG expected not nil") 825 | } 826 | 827 | descriptor = FindPixelFormatDescriptorByPixelFormat(PixelFormatNone) 828 | if descriptor != nil { 829 | t.Fatalf("[TestFindPixelFormatDescriptorByPixelFormat] descriptor=%v, NG expected=nil", descriptor) 830 | } 831 | } 832 | 833 | func TestPixelFormatDescriptor_ComponentCount(t *testing.T) { 834 | pixFmt, ok := FindPixelFormatByName("yuv444p") 835 | if !ok { 836 | t.Fatalf("pixel format not found") 837 | } 838 | descriptor := FindPixelFormatDescriptorByPixelFormat(pixFmt) 839 | if descriptor == nil { 840 | t.Fatalf("pixel format descriptor not found") 841 | } 842 | 843 | count := descriptor.ComponentCount() 844 | if count != 3 { 845 | t.Fatalf("[TestPixelFormatDescriptor_ComponentCount] count=%d, NG expected=%d", count, 3) 846 | } 847 | } 848 | -------------------------------------------------------------------------------- /avutil/genhack.go: -------------------------------------------------------------------------------- 1 | package avutil 2 | 3 | // 4 | // THIS FILE WAS AUTOMATICALLY GENERATED by hackgenerator.go. 5 | // 6 | 7 | // 8 | //#include 9 | // 10 | // 11 | //int GO_AVERROR_BSF_NOT_FOUND() 12 | //{ 13 | // return (int)(AVERROR_BSF_NOT_FOUND); 14 | //} 15 | // 16 | //int GO_AVERROR_BUG() 17 | //{ 18 | // return (int)(AVERROR_BUG); 19 | //} 20 | // 21 | //int GO_AVERROR_BUFFER_TOO_SMALL() 22 | //{ 23 | // return (int)(AVERROR_BUFFER_TOO_SMALL); 24 | //} 25 | // 26 | //int GO_AVERROR_DECODER_NOT_FOUND() 27 | //{ 28 | // return (int)(AVERROR_DECODER_NOT_FOUND); 29 | //} 30 | // 31 | //int GO_AVERROR_DEMUXER_NOT_FOUND() 32 | //{ 33 | // return (int)(AVERROR_DEMUXER_NOT_FOUND); 34 | //} 35 | // 36 | //int GO_AVERROR_ENCODER_NOT_FOUND() 37 | //{ 38 | // return (int)(AVERROR_ENCODER_NOT_FOUND); 39 | //} 40 | // 41 | //int GO_AVERROR_EOF() 42 | //{ 43 | // return (int)(AVERROR_EOF); 44 | //} 45 | // 46 | //int GO_AVERROR_EXIT() 47 | //{ 48 | // return (int)(AVERROR_EXIT); 49 | //} 50 | // 51 | //int GO_AVERROR_EXTERNAL() 52 | //{ 53 | // return (int)(AVERROR_EXTERNAL); 54 | //} 55 | // 56 | //int GO_AVERROR_FILTER_NOT_FOUND() 57 | //{ 58 | // return (int)(AVERROR_FILTER_NOT_FOUND); 59 | //} 60 | // 61 | //int GO_AVERROR_INVALIDDATA() 62 | //{ 63 | // return (int)(AVERROR_INVALIDDATA); 64 | //} 65 | // 66 | //int GO_AVERROR_MUXER_NOT_FOUND() 67 | //{ 68 | // return (int)(AVERROR_MUXER_NOT_FOUND); 69 | //} 70 | // 71 | //int GO_AVERROR_OPTION_NOT_FOUND() 72 | //{ 73 | // return (int)(AVERROR_OPTION_NOT_FOUND); 74 | //} 75 | // 76 | //int GO_AVERROR_PATCHWELCOME() 77 | //{ 78 | // return (int)(AVERROR_PATCHWELCOME); 79 | //} 80 | // 81 | //int GO_AVERROR_PROTOCOL_NOT_FOUND() 82 | //{ 83 | // return (int)(AVERROR_PROTOCOL_NOT_FOUND); 84 | //} 85 | // 86 | //int GO_AVERROR_STREAM_NOT_FOUND() 87 | //{ 88 | // return (int)(AVERROR_STREAM_NOT_FOUND); 89 | //} 90 | // 91 | //int GO_AVERROR_BUG2() 92 | //{ 93 | // return (int)(AVERROR_BUG2); 94 | //} 95 | // 96 | //int GO_AVERROR_UNKNOWN() 97 | //{ 98 | // return (int)(AVERROR_UNKNOWN); 99 | //} 100 | // 101 | //int GO_AVERROR_EXPERIMENTAL() 102 | //{ 103 | // return (int)(AVERROR_EXPERIMENTAL); 104 | //} 105 | // 106 | //int GO_AVERROR_INPUT_CHANGED() 107 | //{ 108 | // return (int)(AVERROR_INPUT_CHANGED); 109 | //} 110 | // 111 | //int GO_AVERROR_OUTPUT_CHANGED() 112 | //{ 113 | // return (int)(AVERROR_OUTPUT_CHANGED); 114 | //} 115 | // 116 | //int GO_AVERROR_HTTP_BAD_REQUEST() 117 | //{ 118 | // return (int)(AVERROR_HTTP_BAD_REQUEST); 119 | //} 120 | // 121 | //int GO_AVERROR_HTTP_UNAUTHORIZED() 122 | //{ 123 | // return (int)(AVERROR_HTTP_UNAUTHORIZED); 124 | //} 125 | // 126 | //int GO_AVERROR_HTTP_FORBIDDEN() 127 | //{ 128 | // return (int)(AVERROR_HTTP_FORBIDDEN); 129 | //} 130 | // 131 | //int GO_AVERROR_HTTP_NOT_FOUND() 132 | //{ 133 | // return (int)(AVERROR_HTTP_NOT_FOUND); 134 | //} 135 | // 136 | //int GO_AVERROR_HTTP_OTHER_4XX() 137 | //{ 138 | // return (int)(AVERROR_HTTP_OTHER_4XX); 139 | //} 140 | // 141 | //int GO_AVERROR_HTTP_SERVER_ERROR() 142 | //{ 143 | // return (int)(AVERROR_HTTP_SERVER_ERROR); 144 | //} 145 | // 146 | //int64_t GO_AV_NOPTS_VALUE() 147 | //{ 148 | // return (int64_t)(AV_NOPTS_VALUE); 149 | //} 150 | // 151 | // #cgo pkg-config: libavutil 152 | import "C" 153 | 154 | const ( 155 | ErrorCodeBSFNotFound ErrorCode = -1179861752 156 | ErrorCodeBug ErrorCode = -558323010 157 | ErrorCodeBufferTooSmall ErrorCode = -1397118274 158 | ErrorCodeDecoderNotFound ErrorCode = -1128613112 159 | ErrorCodeDemuxerNotFound ErrorCode = -1296385272 160 | ErrorCodeEncoderNotFound ErrorCode = -1129203192 161 | ErrorCodeEOF ErrorCode = -541478725 162 | ErrorCodeExit ErrorCode = -1414092869 163 | ErrorCodeExternal ErrorCode = -542398533 164 | ErrorCodeFilterNotFound ErrorCode = -1279870712 165 | ErrorCodeInvalidData ErrorCode = -1094995529 166 | ErrorCodeMuxerNotFound ErrorCode = -1481985528 167 | ErrorCodeOptionNotFound ErrorCode = -1414549496 168 | ErrorCodePatchWelcome ErrorCode = -1163346256 169 | ErrorCodeProtocolNotFound ErrorCode = -1330794744 170 | ErrorCodeStreamNotFound ErrorCode = -1381258232 171 | ErrorCodeBug2 ErrorCode = -541545794 172 | ErrorCodeUnknown ErrorCode = -1313558101 173 | ErrorCodeExperimental ErrorCode = -733130664 174 | ErrorCodeInputChanged ErrorCode = -1668179713 175 | ErrorCodeOutputChanged ErrorCode = -1668179714 176 | ErrorCodeHttpBadRequest ErrorCode = -808465656 177 | ErrorCodeHttpUnauthorized ErrorCode = -825242872 178 | ErrorCodeHttpForbidden ErrorCode = -858797304 179 | ErrorCodeHttpNotFound ErrorCode = -875574520 180 | ErrorCodeHttpOther4xx ErrorCode = -1482175736 181 | ErrorCodeHttpServerError ErrorCode = -1482175992 182 | NoPTSValue int64 = -9223372036854775808 183 | ) 184 | 185 | func getErrorCodeBSFNotFound() C.int { 186 | return C.GO_AVERROR_BSF_NOT_FOUND() 187 | } 188 | 189 | func getErrorCodeBug() C.int { 190 | return C.GO_AVERROR_BUG() 191 | } 192 | 193 | func getErrorCodeBufferTooSmall() C.int { 194 | return C.GO_AVERROR_BUFFER_TOO_SMALL() 195 | } 196 | 197 | func getErrorCodeDecoderNotFound() C.int { 198 | return C.GO_AVERROR_DECODER_NOT_FOUND() 199 | } 200 | 201 | func getErrorCodeDemuxerNotFound() C.int { 202 | return C.GO_AVERROR_DEMUXER_NOT_FOUND() 203 | } 204 | 205 | func getErrorCodeEncoderNotFound() C.int { 206 | return C.GO_AVERROR_ENCODER_NOT_FOUND() 207 | } 208 | 209 | func getErrorCodeEOF() C.int { 210 | return C.GO_AVERROR_EOF() 211 | } 212 | 213 | func getErrorCodeExit() C.int { 214 | return C.GO_AVERROR_EXIT() 215 | } 216 | 217 | func getErrorCodeExternal() C.int { 218 | return C.GO_AVERROR_EXTERNAL() 219 | } 220 | 221 | func getErrorCodeFilterNotFound() C.int { 222 | return C.GO_AVERROR_FILTER_NOT_FOUND() 223 | } 224 | 225 | func getErrorCodeInvalidData() C.int { 226 | return C.GO_AVERROR_INVALIDDATA() 227 | } 228 | 229 | func getErrorCodeMuxerNotFound() C.int { 230 | return C.GO_AVERROR_MUXER_NOT_FOUND() 231 | } 232 | 233 | func getErrorCodeOptionNotFound() C.int { 234 | return C.GO_AVERROR_OPTION_NOT_FOUND() 235 | } 236 | 237 | func getErrorCodePatchWelcome() C.int { 238 | return C.GO_AVERROR_PATCHWELCOME() 239 | } 240 | 241 | func getErrorCodeProtocolNotFound() C.int { 242 | return C.GO_AVERROR_PROTOCOL_NOT_FOUND() 243 | } 244 | 245 | func getErrorCodeStreamNotFound() C.int { 246 | return C.GO_AVERROR_STREAM_NOT_FOUND() 247 | } 248 | 249 | func getErrorCodeBug2() C.int { 250 | return C.GO_AVERROR_BUG2() 251 | } 252 | 253 | func getErrorCodeUnknown() C.int { 254 | return C.GO_AVERROR_UNKNOWN() 255 | } 256 | 257 | func getErrorCodeExperimental() C.int { 258 | return C.GO_AVERROR_EXPERIMENTAL() 259 | } 260 | 261 | func getErrorCodeInputChanged() C.int { 262 | return C.GO_AVERROR_INPUT_CHANGED() 263 | } 264 | 265 | func getErrorCodeOutputChanged() C.int { 266 | return C.GO_AVERROR_OUTPUT_CHANGED() 267 | } 268 | 269 | func getErrorCodeHttpBadRequest() C.int { 270 | return C.GO_AVERROR_HTTP_BAD_REQUEST() 271 | } 272 | 273 | func getErrorCodeHttpUnauthorized() C.int { 274 | return C.GO_AVERROR_HTTP_UNAUTHORIZED() 275 | } 276 | 277 | func getErrorCodeHttpForbidden() C.int { 278 | return C.GO_AVERROR_HTTP_FORBIDDEN() 279 | } 280 | 281 | func getErrorCodeHttpNotFound() C.int { 282 | return C.GO_AVERROR_HTTP_NOT_FOUND() 283 | } 284 | 285 | func getErrorCodeHttpOther4xx() C.int { 286 | return C.GO_AVERROR_HTTP_OTHER_4XX() 287 | } 288 | 289 | func getErrorCodeHttpServerError() C.int { 290 | return C.GO_AVERROR_HTTP_SERVER_ERROR() 291 | } 292 | 293 | func getNoPTSValue() C.int64_t { 294 | return C.GO_AV_NOPTS_VALUE() 295 | } 296 | -------------------------------------------------------------------------------- /avutil/genhack_test.go: -------------------------------------------------------------------------------- 1 | package avutil 2 | 3 | // 4 | // THIS FILE WAS AUTOMATICALLY GENERATED by hackgenerator.go. 5 | // 6 | 7 | import "testing" 8 | 9 | func TestHackOverflowErrorCodeBSFNotFound(t *testing.T) { 10 | cval := ErrorCode(getErrorCodeBSFNotFound()) 11 | if cval != ErrorCodeBSFNotFound { 12 | t.Fatalf("ErrorCodeBSFNotFound/AVERROR_BSF_NOT_FOUND overflow check failed: expected %v but got %v", ErrorCodeBSFNotFound, cval) 13 | } 14 | } 15 | 16 | func TestHackOverflowErrorCodeBug(t *testing.T) { 17 | cval := ErrorCode(getErrorCodeBug()) 18 | if cval != ErrorCodeBug { 19 | t.Fatalf("ErrorCodeBug/AVERROR_BUG overflow check failed: expected %v but got %v", ErrorCodeBug, cval) 20 | } 21 | } 22 | 23 | func TestHackOverflowErrorCodeBufferTooSmall(t *testing.T) { 24 | cval := ErrorCode(getErrorCodeBufferTooSmall()) 25 | if cval != ErrorCodeBufferTooSmall { 26 | t.Fatalf("ErrorCodeBufferTooSmall/AVERROR_BUFFER_TOO_SMALL overflow check failed: expected %v but got %v", ErrorCodeBufferTooSmall, cval) 27 | } 28 | } 29 | 30 | func TestHackOverflowErrorCodeDecoderNotFound(t *testing.T) { 31 | cval := ErrorCode(getErrorCodeDecoderNotFound()) 32 | if cval != ErrorCodeDecoderNotFound { 33 | t.Fatalf("ErrorCodeDecoderNotFound/AVERROR_DECODER_NOT_FOUND overflow check failed: expected %v but got %v", ErrorCodeDecoderNotFound, cval) 34 | } 35 | } 36 | 37 | func TestHackOverflowErrorCodeDemuxerNotFound(t *testing.T) { 38 | cval := ErrorCode(getErrorCodeDemuxerNotFound()) 39 | if cval != ErrorCodeDemuxerNotFound { 40 | t.Fatalf("ErrorCodeDemuxerNotFound/AVERROR_DEMUXER_NOT_FOUND overflow check failed: expected %v but got %v", ErrorCodeDemuxerNotFound, cval) 41 | } 42 | } 43 | 44 | func TestHackOverflowErrorCodeEncoderNotFound(t *testing.T) { 45 | cval := ErrorCode(getErrorCodeEncoderNotFound()) 46 | if cval != ErrorCodeEncoderNotFound { 47 | t.Fatalf("ErrorCodeEncoderNotFound/AVERROR_ENCODER_NOT_FOUND overflow check failed: expected %v but got %v", ErrorCodeEncoderNotFound, cval) 48 | } 49 | } 50 | 51 | func TestHackOverflowErrorCodeEOF(t *testing.T) { 52 | cval := ErrorCode(getErrorCodeEOF()) 53 | if cval != ErrorCodeEOF { 54 | t.Fatalf("ErrorCodeEOF/AVERROR_EOF overflow check failed: expected %v but got %v", ErrorCodeEOF, cval) 55 | } 56 | } 57 | 58 | func TestHackOverflowErrorCodeExit(t *testing.T) { 59 | cval := ErrorCode(getErrorCodeExit()) 60 | if cval != ErrorCodeExit { 61 | t.Fatalf("ErrorCodeExit/AVERROR_EXIT overflow check failed: expected %v but got %v", ErrorCodeExit, cval) 62 | } 63 | } 64 | 65 | func TestHackOverflowErrorCodeExternal(t *testing.T) { 66 | cval := ErrorCode(getErrorCodeExternal()) 67 | if cval != ErrorCodeExternal { 68 | t.Fatalf("ErrorCodeExternal/AVERROR_EXTERNAL overflow check failed: expected %v but got %v", ErrorCodeExternal, cval) 69 | } 70 | } 71 | 72 | func TestHackOverflowErrorCodeFilterNotFound(t *testing.T) { 73 | cval := ErrorCode(getErrorCodeFilterNotFound()) 74 | if cval != ErrorCodeFilterNotFound { 75 | t.Fatalf("ErrorCodeFilterNotFound/AVERROR_FILTER_NOT_FOUND overflow check failed: expected %v but got %v", ErrorCodeFilterNotFound, cval) 76 | } 77 | } 78 | 79 | func TestHackOverflowErrorCodeInvalidData(t *testing.T) { 80 | cval := ErrorCode(getErrorCodeInvalidData()) 81 | if cval != ErrorCodeInvalidData { 82 | t.Fatalf("ErrorCodeInvalidData/AVERROR_INVALIDDATA overflow check failed: expected %v but got %v", ErrorCodeInvalidData, cval) 83 | } 84 | } 85 | 86 | func TestHackOverflowErrorCodeMuxerNotFound(t *testing.T) { 87 | cval := ErrorCode(getErrorCodeMuxerNotFound()) 88 | if cval != ErrorCodeMuxerNotFound { 89 | t.Fatalf("ErrorCodeMuxerNotFound/AVERROR_MUXER_NOT_FOUND overflow check failed: expected %v but got %v", ErrorCodeMuxerNotFound, cval) 90 | } 91 | } 92 | 93 | func TestHackOverflowErrorCodeOptionNotFound(t *testing.T) { 94 | cval := ErrorCode(getErrorCodeOptionNotFound()) 95 | if cval != ErrorCodeOptionNotFound { 96 | t.Fatalf("ErrorCodeOptionNotFound/AVERROR_OPTION_NOT_FOUND overflow check failed: expected %v but got %v", ErrorCodeOptionNotFound, cval) 97 | } 98 | } 99 | 100 | func TestHackOverflowErrorCodePatchWelcome(t *testing.T) { 101 | cval := ErrorCode(getErrorCodePatchWelcome()) 102 | if cval != ErrorCodePatchWelcome { 103 | t.Fatalf("ErrorCodePatchWelcome/AVERROR_PATCHWELCOME overflow check failed: expected %v but got %v", ErrorCodePatchWelcome, cval) 104 | } 105 | } 106 | 107 | func TestHackOverflowErrorCodeProtocolNotFound(t *testing.T) { 108 | cval := ErrorCode(getErrorCodeProtocolNotFound()) 109 | if cval != ErrorCodeProtocolNotFound { 110 | t.Fatalf("ErrorCodeProtocolNotFound/AVERROR_PROTOCOL_NOT_FOUND overflow check failed: expected %v but got %v", ErrorCodeProtocolNotFound, cval) 111 | } 112 | } 113 | 114 | func TestHackOverflowErrorCodeStreamNotFound(t *testing.T) { 115 | cval := ErrorCode(getErrorCodeStreamNotFound()) 116 | if cval != ErrorCodeStreamNotFound { 117 | t.Fatalf("ErrorCodeStreamNotFound/AVERROR_STREAM_NOT_FOUND overflow check failed: expected %v but got %v", ErrorCodeStreamNotFound, cval) 118 | } 119 | } 120 | 121 | func TestHackOverflowErrorCodeBug2(t *testing.T) { 122 | cval := ErrorCode(getErrorCodeBug2()) 123 | if cval != ErrorCodeBug2 { 124 | t.Fatalf("ErrorCodeBug2/AVERROR_BUG2 overflow check failed: expected %v but got %v", ErrorCodeBug2, cval) 125 | } 126 | } 127 | 128 | func TestHackOverflowErrorCodeUnknown(t *testing.T) { 129 | cval := ErrorCode(getErrorCodeUnknown()) 130 | if cval != ErrorCodeUnknown { 131 | t.Fatalf("ErrorCodeUnknown/AVERROR_UNKNOWN overflow check failed: expected %v but got %v", ErrorCodeUnknown, cval) 132 | } 133 | } 134 | 135 | func TestHackOverflowErrorCodeExperimental(t *testing.T) { 136 | cval := ErrorCode(getErrorCodeExperimental()) 137 | if cval != ErrorCodeExperimental { 138 | t.Fatalf("ErrorCodeExperimental/AVERROR_EXPERIMENTAL overflow check failed: expected %v but got %v", ErrorCodeExperimental, cval) 139 | } 140 | } 141 | 142 | func TestHackOverflowErrorCodeInputChanged(t *testing.T) { 143 | cval := ErrorCode(getErrorCodeInputChanged()) 144 | if cval != ErrorCodeInputChanged { 145 | t.Fatalf("ErrorCodeInputChanged/AVERROR_INPUT_CHANGED overflow check failed: expected %v but got %v", ErrorCodeInputChanged, cval) 146 | } 147 | } 148 | 149 | func TestHackOverflowErrorCodeOutputChanged(t *testing.T) { 150 | cval := ErrorCode(getErrorCodeOutputChanged()) 151 | if cval != ErrorCodeOutputChanged { 152 | t.Fatalf("ErrorCodeOutputChanged/AVERROR_OUTPUT_CHANGED overflow check failed: expected %v but got %v", ErrorCodeOutputChanged, cval) 153 | } 154 | } 155 | 156 | func TestHackOverflowErrorCodeHttpBadRequest(t *testing.T) { 157 | cval := ErrorCode(getErrorCodeHttpBadRequest()) 158 | if cval != ErrorCodeHttpBadRequest { 159 | t.Fatalf("ErrorCodeHttpBadRequest/AVERROR_HTTP_BAD_REQUEST overflow check failed: expected %v but got %v", ErrorCodeHttpBadRequest, cval) 160 | } 161 | } 162 | 163 | func TestHackOverflowErrorCodeHttpUnauthorized(t *testing.T) { 164 | cval := ErrorCode(getErrorCodeHttpUnauthorized()) 165 | if cval != ErrorCodeHttpUnauthorized { 166 | t.Fatalf("ErrorCodeHttpUnauthorized/AVERROR_HTTP_UNAUTHORIZED overflow check failed: expected %v but got %v", ErrorCodeHttpUnauthorized, cval) 167 | } 168 | } 169 | 170 | func TestHackOverflowErrorCodeHttpForbidden(t *testing.T) { 171 | cval := ErrorCode(getErrorCodeHttpForbidden()) 172 | if cval != ErrorCodeHttpForbidden { 173 | t.Fatalf("ErrorCodeHttpForbidden/AVERROR_HTTP_FORBIDDEN overflow check failed: expected %v but got %v", ErrorCodeHttpForbidden, cval) 174 | } 175 | } 176 | 177 | func TestHackOverflowErrorCodeHttpNotFound(t *testing.T) { 178 | cval := ErrorCode(getErrorCodeHttpNotFound()) 179 | if cval != ErrorCodeHttpNotFound { 180 | t.Fatalf("ErrorCodeHttpNotFound/AVERROR_HTTP_NOT_FOUND overflow check failed: expected %v but got %v", ErrorCodeHttpNotFound, cval) 181 | } 182 | } 183 | 184 | func TestHackOverflowErrorCodeHttpOther4xx(t *testing.T) { 185 | cval := ErrorCode(getErrorCodeHttpOther4xx()) 186 | if cval != ErrorCodeHttpOther4xx { 187 | t.Fatalf("ErrorCodeHttpOther4xx/AVERROR_HTTP_OTHER_4XX overflow check failed: expected %v but got %v", ErrorCodeHttpOther4xx, cval) 188 | } 189 | } 190 | 191 | func TestHackOverflowErrorCodeHttpServerError(t *testing.T) { 192 | cval := ErrorCode(getErrorCodeHttpServerError()) 193 | if cval != ErrorCodeHttpServerError { 194 | t.Fatalf("ErrorCodeHttpServerError/AVERROR_HTTP_SERVER_ERROR overflow check failed: expected %v but got %v", ErrorCodeHttpServerError, cval) 195 | } 196 | } 197 | 198 | func TestHackOverflowNoPTSValue(t *testing.T) { 199 | cval := int64(getNoPTSValue()) 200 | if cval != NoPTSValue { 201 | t.Fatalf("NoPTSValue/AV_NOPTS_VALUE overflow check failed: expected %v but got %v", NoPTSValue, cval) 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /avutil/hackgenerator.go: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | package main 4 | 5 | import ( 6 | "bytes" 7 | "fmt" 8 | "go/format" 9 | "log" 10 | "os" 11 | ) 12 | 13 | var goBuf bytes.Buffer 14 | var goTestBuf bytes.Buffer 15 | 16 | type cdef struct { 17 | cName string 18 | goName string 19 | cType string 20 | goType string 21 | val interface{} 22 | } 23 | 24 | var cdefs = []cdef{ 25 | // AVERROR_* 26 | {cName: "AVERROR_BSF_NOT_FOUND", goName: "ErrorCodeBSFNotFound", cType: "int", goType: "ErrorCode", val: -1179861752}, 27 | {cName: "AVERROR_BUG", goName: "ErrorCodeBug", cType: "int", goType: "ErrorCode", val: -558323010}, 28 | {cName: "AVERROR_BUFFER_TOO_SMALL", goName: "ErrorCodeBufferTooSmall", cType: "int", goType: "ErrorCode", val: -1397118274}, 29 | {cName: "AVERROR_DECODER_NOT_FOUND", goName: "ErrorCodeDecoderNotFound", cType: "int", goType: "ErrorCode", val: -1128613112}, 30 | {cName: "AVERROR_DEMUXER_NOT_FOUND", goName: "ErrorCodeDemuxerNotFound", cType: "int", goType: "ErrorCode", val: -1296385272}, 31 | {cName: "AVERROR_ENCODER_NOT_FOUND", goName: "ErrorCodeEncoderNotFound", cType: "int", goType: "ErrorCode", val: -1129203192}, 32 | {cName: "AVERROR_EOF", goName: "ErrorCodeEOF", cType: "int", goType: "ErrorCode", val: -541478725}, 33 | {cName: "AVERROR_EXIT", goName: "ErrorCodeExit", cType: "int", goType: "ErrorCode", val: -1414092869}, 34 | {cName: "AVERROR_EXTERNAL", goName: "ErrorCodeExternal", cType: "int", goType: "ErrorCode", val: -542398533}, 35 | {cName: "AVERROR_FILTER_NOT_FOUND", goName: "ErrorCodeFilterNotFound", cType: "int", goType: "ErrorCode", val: -1279870712}, 36 | {cName: "AVERROR_INVALIDDATA", goName: "ErrorCodeInvalidData", cType: "int", goType: "ErrorCode", val: -1094995529}, 37 | {cName: "AVERROR_MUXER_NOT_FOUND", goName: "ErrorCodeMuxerNotFound", cType: "int", goType: "ErrorCode", val: -1481985528}, 38 | {cName: "AVERROR_OPTION_NOT_FOUND", goName: "ErrorCodeOptionNotFound", cType: "int", goType: "ErrorCode", val: -1414549496}, 39 | {cName: "AVERROR_PATCHWELCOME", goName: "ErrorCodePatchWelcome", cType: "int", goType: "ErrorCode", val: -1163346256}, 40 | {cName: "AVERROR_PROTOCOL_NOT_FOUND", goName: "ErrorCodeProtocolNotFound", cType: "int", goType: "ErrorCode", val: -1330794744}, 41 | {cName: "AVERROR_STREAM_NOT_FOUND", goName: "ErrorCodeStreamNotFound", cType: "int", goType: "ErrorCode", val: -1381258232}, 42 | {cName: "AVERROR_BUG2", goName: "ErrorCodeBug2", cType: "int", goType: "ErrorCode", val: -541545794}, 43 | {cName: "AVERROR_UNKNOWN", goName: "ErrorCodeUnknown", cType: "int", goType: "ErrorCode", val: -1313558101}, 44 | {cName: "AVERROR_EXPERIMENTAL", goName: "ErrorCodeExperimental", cType: "int", goType: "ErrorCode", val: -733130664}, 45 | {cName: "AVERROR_INPUT_CHANGED", goName: "ErrorCodeInputChanged", cType: "int", goType: "ErrorCode", val: -1668179713}, 46 | {cName: "AVERROR_OUTPUT_CHANGED", goName: "ErrorCodeOutputChanged", cType: "int", goType: "ErrorCode", val: -1668179714}, 47 | {cName: "AVERROR_HTTP_BAD_REQUEST", goName: "ErrorCodeHttpBadRequest", cType: "int", goType: "ErrorCode", val: -808465656}, 48 | {cName: "AVERROR_HTTP_UNAUTHORIZED", goName: "ErrorCodeHttpUnauthorized", cType: "int", goType: "ErrorCode", val: -825242872}, 49 | {cName: "AVERROR_HTTP_FORBIDDEN", goName: "ErrorCodeHttpForbidden", cType: "int", goType: "ErrorCode", val: -858797304}, 50 | {cName: "AVERROR_HTTP_NOT_FOUND", goName: "ErrorCodeHttpNotFound", cType: "int", goType: "ErrorCode", val: -875574520}, 51 | {cName: "AVERROR_HTTP_OTHER_4XX", goName: "ErrorCodeHttpOther4xx", cType: "int", goType: "ErrorCode", val: -1482175736}, 52 | {cName: "AVERROR_HTTP_SERVER_ERROR", goName: "ErrorCodeHttpServerError", cType: "int", goType: "ErrorCode", val: -1482175992}, 53 | // AV_* 54 | {cName: "AV_NOPTS_VALUE", goName: "NoPTSValue", cType: "int64_t", goType: "int64", val: -9223372036854775808}, 55 | } 56 | 57 | func main() { 58 | writeGo() 59 | writeGoTest() 60 | } 61 | 62 | func die(err error) { 63 | if err != nil { 64 | log.Fatal(err) 65 | } 66 | } 67 | 68 | func printGo(a ...interface{}) { 69 | _, err := fmt.Fprintf(&goBuf, "%s", a...) 70 | die(err) 71 | } 72 | 73 | func writeGo() { 74 | str := `package avutil 75 | // 76 | // THIS FILE WAS AUTOMATICALLY GENERATED by hackgenerator.go. 77 | // 78 | ` 79 | printGo(str) 80 | 81 | str = ` 82 | // 83 | //#include 84 | // 85 | ` 86 | printGo(str) 87 | 88 | for _, d := range cdefs { 89 | printGo("//\n") 90 | printGo(fmt.Sprintf("//%s GO_%s()\n", d.cType, d.cName)) 91 | printGo("//{\n") 92 | printGo(fmt.Sprintf("// return (%s)(%s);\n", d.cType, d.cName)) 93 | printGo("//}\n") 94 | } 95 | 96 | str = `// 97 | // #cgo pkg-config: libavutil 98 | import "C" 99 | ` 100 | printGo(str) 101 | 102 | printGo("\n") 103 | printGo("const (\n") 104 | for _, d := range cdefs { 105 | printGo(fmt.Sprintf("%s %s = %d\n", d.goName, d.goType, d.val)) 106 | } 107 | printGo(")\n") 108 | 109 | printGo("\n") 110 | for _, d := range cdefs { 111 | printGo("\n") 112 | printGo(fmt.Sprintf("func get%s() C.%s {\n", d.goName, d.cType)) 113 | printGo(fmt.Sprintf(" return C.GO_%s()\n", d.cName)) 114 | printGo("}\n") 115 | } 116 | 117 | f, err := os.Create("genhack.go") 118 | die(err) 119 | src, err := format.Source(goBuf.Bytes()) 120 | die(err) 121 | _, err = f.Write(src) 122 | die(err) 123 | die(f.Close()) 124 | } 125 | 126 | func printGoTest(a ...interface{}) { 127 | _, err := fmt.Fprintf(&goTestBuf, "%s", a...) 128 | die(err) 129 | } 130 | 131 | func writeGoTest() { 132 | str := `package avutil 133 | // 134 | // THIS FILE WAS AUTOMATICALLY GENERATED by hackgenerator.go. 135 | // 136 | 137 | import "testing" 138 | ` 139 | printGoTest(str) 140 | 141 | printGoTest("\n") 142 | for _, d := range cdefs { 143 | printGoTest("\n") 144 | printGoTest(fmt.Sprintf("func TestHackOverflow%s(t *testing.T) {\n", d.goName)) 145 | printGoTest(fmt.Sprintf("cval := %s(get%s())\n", d.goType, d.goName)) 146 | printGoTest(fmt.Sprintf("if cval != %s {\n", d.goName)) 147 | printGoTest(fmt.Sprintf("t.Fatalf(\"%s/%s overflow check failed: expected %%v but got %%v\", %s, cval)", d.goName, d.cName, d.goName)) 148 | printGoTest("}\n") 149 | printGoTest("}\n") 150 | } 151 | 152 | f, err := os.Create("genhack_test.go") 153 | die(err) 154 | src, err := format.Source(goTestBuf.Bytes()) 155 | die(err) 156 | _, err = f.Write(src) 157 | die(err) 158 | die(f.Close()) 159 | } 160 | -------------------------------------------------------------------------------- /examples/transcoder.go: -------------------------------------------------------------------------------- 1 | // In this example, we show a very simplistic way of transcoding the frames of 2 | // the first video stream of an input file. 3 | // Please note that transcoding is a difficult subject and full of corner 4 | // cases. This sample is far from perfect, and it will easily break/crash 5 | // depending on the specified input and output files. 6 | // 7 | // Tested with 8 | // 9 | // go run transcoder.go --input=https://bintray.com/imkira/go-libav/download_file?file_path=sample_iPod.m4v --output=output.mp4 10 | // go run transcoder.go --input=https://bintray.com/imkira/go-libav/download_file?file_path=sample_iPod.m4v --output=output.avi 11 | package main 12 | 13 | import ( 14 | "flag" 15 | "log" 16 | 17 | "github.com/imkira/go-libav/avcodec" 18 | "github.com/imkira/go-libav/avfilter" 19 | "github.com/imkira/go-libav/avformat" 20 | "github.com/imkira/go-libav/avutil" 21 | ) 22 | 23 | var inputFileName, outputFileName string 24 | 25 | func init() { 26 | flag.StringVar(&inputFileName, "input", "", "source file to decode") 27 | flag.StringVar(&outputFileName, "output", "", "target file to encode") 28 | flag.Parse() 29 | } 30 | 31 | func main() { 32 | if len(inputFileName) == 0 { 33 | log.Fatalf("Missing --input=file\n") 34 | } 35 | if len(outputFileName) == 0 { 36 | log.Fatalf("Missing --output=file\n") 37 | } 38 | 39 | avutil.SetLogLevel(avutil.LogLevelDebug) 40 | 41 | // first, we set up a context for decoding 42 | ctx, err := newContext() 43 | if err != nil { 44 | log.Fatalf("Failed to create context: %v\n", err) 45 | } 46 | defer ctx.free() 47 | 48 | // open input file 49 | openInput(ctx) 50 | 51 | // open output file 52 | openOutput(ctx) 53 | writeHeader(ctx) 54 | 55 | // enter transcode loop 56 | reading, writing := true, true 57 | for reading || writing { 58 | reading = decodeStream(ctx) 59 | writing = encodeStream(ctx) 60 | } 61 | 62 | writeTrailer(ctx) 63 | } 64 | 65 | func openInput(ctx *context) { 66 | var err error 67 | 68 | // open format (container) context 69 | ctx.decFmt, err = avformat.NewContextForInput() 70 | if err != nil { 71 | log.Fatalf("Failed to open input context: %v\n", err) 72 | } 73 | 74 | // set some options for opening file 75 | options := avutil.NewDictionary() 76 | defer options.Free() 77 | if err := options.Set("scan_all_pmts", "1"); err != nil { 78 | log.Fatalf("Failed to set input options: %v\n", err) 79 | } 80 | 81 | // open file for decoding 82 | if err := ctx.decFmt.OpenInput(inputFileName, nil, options); err != nil { 83 | log.Fatalf("Failed to open input file: %v\n", err) 84 | } 85 | 86 | // initialize context with stream information 87 | if err := ctx.decFmt.FindStreamInfo(nil); err != nil { 88 | log.Fatalf("Failed to find stream info: %v\n", err) 89 | } 90 | 91 | // dump streams to standard output 92 | ctx.decFmt.Dump(0, inputFileName, false) 93 | 94 | // prepare first video stream for decoding 95 | openFirstInputVideoStream(ctx) 96 | } 97 | 98 | func openFirstInputVideoStream(ctx *context) { 99 | var err error 100 | 101 | // find first video stream 102 | if ctx.decStream = firstVideoStream(ctx.decFmt); ctx.decStream == nil { 103 | log.Fatalf("Could not find a video stream. Aborting...\n") 104 | } 105 | 106 | codecCtx := ctx.decStream.CodecContext() 107 | codec := avcodec.FindDecoderByID(codecCtx.CodecID()) 108 | if codec == nil { 109 | log.Fatalf("Could not find decoder: %v\n", codecCtx.CodecID()) 110 | } 111 | if ctx.decCodec, err = avcodec.NewContextWithCodec(codec); err != nil { 112 | log.Fatalf("Failed to create codec context: %v\n", err) 113 | } 114 | if err := codecCtx.CopyTo(ctx.decCodec); err != nil { 115 | log.Fatalf("Failed to copy codec context: %v\n", err) 116 | } 117 | if err := ctx.decCodec.SetInt64Option("refcounted_frames", 1); err != nil { 118 | log.Fatalf("Failed to copy codec context: %v\n", err) 119 | } 120 | if err := ctx.decCodec.OpenWithCodec(codec, nil); err != nil { 121 | log.Fatalf("Failed to open codec: %v\n", err) 122 | } 123 | 124 | // we need a video filter to push the decoded frames to 125 | ctx.srcFilter = addFilter(ctx, "buffer", "in") 126 | if err = ctx.srcFilter.SetImageSizeOption("video_size", ctx.decCodec.Width(), ctx.decCodec.Height()); err != nil { 127 | log.Fatalf("Failed to set filter option: %v\n", err) 128 | } 129 | if err = ctx.srcFilter.SetPixelFormatOption("pix_fmt", ctx.decCodec.PixelFormat()); err != nil { 130 | log.Fatalf("Failed to set filter option: %v\n", err) 131 | } 132 | if err = ctx.srcFilter.SetRationalOption("time_base", ctx.decCodec.TimeBase()); err != nil { 133 | log.Fatalf("Failed to set filter option: %v\n", err) 134 | } 135 | if err = ctx.srcFilter.Init(); err != nil { 136 | log.Fatalf("Failed to initialize buffer filter: %v\n", err) 137 | } 138 | } 139 | 140 | func firstVideoStream(fmtCtx *avformat.Context) *avformat.Stream { 141 | for _, stream := range fmtCtx.Streams() { 142 | switch stream.CodecContext().CodecType() { 143 | case avutil.MediaTypeVideo: 144 | return stream 145 | } 146 | } 147 | return nil 148 | } 149 | 150 | func openOutput(ctx *context) { 151 | var err error 152 | 153 | // guess format given output filename 154 | fmt := avformat.GuessOutputFromFileName(outputFileName) 155 | if fmt == nil { 156 | log.Fatalf("Failed to guess output for output file: %s\n", outputFileName) 157 | } 158 | if ctx.encFmt, err = avformat.NewContextForOutput(fmt); err != nil { 159 | log.Fatalf("Failed to open output context: %v\n", err) 160 | } 161 | ctx.encFmt.SetFileName(outputFileName) 162 | 163 | // prepare first video stream for encoding 164 | openOutputVideoStream(ctx, fmt) 165 | 166 | if fmt.Flags()&avformat.FlagNoFile != 0 { 167 | return 168 | } 169 | 170 | // prepare I/O 171 | flags := avformat.IOFlagWrite 172 | if ctx.encIO, err = avformat.OpenIOContext(outputFileName, flags, nil, nil); err != nil { 173 | log.Fatalf("Failed to open I/O context: %v\n", err) 174 | } 175 | ctx.encFmt.SetIOContext(ctx.encIO) 176 | } 177 | 178 | func openOutputVideoStream(ctx *context, fmt *avformat.Output) { 179 | var err error 180 | 181 | ctx.encStream, err = ctx.encFmt.NewStreamWithCodec(nil) 182 | if err != nil { 183 | log.Fatalf("Failed to open output video stream: %v\n", err) 184 | } 185 | codecCtx := ctx.encStream.CodecContext() 186 | codecCtx.SetCodecType(avutil.MediaTypeVideo) 187 | codecID := fmt.GuessCodecID(outputFileName, codecCtx.CodecType()) 188 | codec := avcodec.FindEncoderByID(codecID) 189 | if codec == nil { 190 | log.Fatalf("Could not find encoder: %v\n", codecID) 191 | } 192 | if ctx.encCodec, err = avcodec.NewContextWithCodec(codec); err != nil { 193 | log.Fatalf("Failed to create codec context: %v\n", err) 194 | } 195 | ctx.encCodec.SetCodecType(codecCtx.CodecType()) 196 | 197 | // we need a video filter to pull the encoded frames from 198 | ctx.sinkFilter = addFilter(ctx, "buffersink", "out") 199 | if err = ctx.sinkFilter.Init(); err != nil { 200 | log.Fatalf("Failed to initialize buffersink filter: %v\n", err) 201 | } 202 | if err = ctx.srcFilter.Link(0, ctx.sinkFilter, 0); err != nil { 203 | log.Fatalf("Failed to link filters: %v\n", err) 204 | } 205 | if err = ctx.filterGraph.Config(); err != nil { 206 | log.Fatalf("Failed to config filter graph: %v\n", err) 207 | } 208 | 209 | sinkPads := ctx.sinkFilter.Inputs() 210 | sinkPad := sinkPads[0] 211 | ctx.encCodec.SetWidth(sinkPad.Width()) 212 | ctx.encCodec.SetHeight(sinkPad.Height()) 213 | ctx.encCodec.SetPixelFormat(sinkPad.PixelFormat()) 214 | ctx.encCodec.SetTimeBase(ctx.decCodec.TimeBase()) 215 | ctx.encCodec.SetStrictStdCompliance(avcodec.ComplianceNormal) 216 | 217 | if fmt.Flags()&avformat.FlagGlobalHeader != 0 { 218 | ctx.encCodec.SetFlags(ctx.encCodec.Flags() | avcodec.FlagGlobalHeader) 219 | } 220 | 221 | if err = ctx.encCodec.OpenWithCodec(codec, nil); err != nil { 222 | log.Fatalf("Failed to open codec: %v\n", err) 223 | } 224 | if err := ctx.encCodec.CopyTo(ctx.encStream.CodecContext()); err != nil { 225 | log.Fatalf("Failed to copy codec context: %v\n", err) 226 | } 227 | ctx.encStream.SetTimeBase(ctx.encCodec.TimeBase()) 228 | ctx.encStream.CodecContext().SetCodec(ctx.encCodec.Codec()) 229 | } 230 | 231 | func decodeStream(ctx *context) bool { 232 | // read packet from input file 233 | reading, err := ctx.decFmt.ReadFrame(ctx.decPkt) 234 | if err != nil { 235 | log.Fatalf("Failed to read packet: %v\n", err) 236 | } 237 | if !reading { 238 | return false 239 | } 240 | defer ctx.decPkt.Unref() 241 | // is this not a packet for the the stream we are interested in? 242 | if ctx.decPkt.StreamIndex() != ctx.decStream.Index() { 243 | return true 244 | } 245 | ctx.decPkt.RescaleTime(ctx.decStream.TimeBase(), ctx.decCodec.TimeBase()) 246 | var decoded bool 247 | for ctx.decPkt.Size() > 0 { 248 | if !decodeFrame(ctx) { 249 | break 250 | } 251 | decoded = true 252 | } 253 | return decoded 254 | } 255 | 256 | func decodeFrame(ctx *context) bool { 257 | ok, size, err := ctx.decCodec.DecodeVideo(ctx.decPkt, ctx.decFrame) 258 | if err != nil { 259 | log.Fatalf("Failed to decode packet: %v\n", err) 260 | } 261 | ctx.decFrame.SetPTS(ctx.decFrame.BestEffortTimestamp()) 262 | if size > 0 { 263 | defer ctx.decPkt.ConsumeData(size) 264 | } 265 | if !ok { 266 | return (size > 0) 267 | } 268 | defer ctx.decFrame.Unref() 269 | pushFrame(ctx) 270 | return true 271 | } 272 | 273 | func pushFrame(ctx *context) { 274 | flags := avfilter.BufferSrcFlagKeepRef 275 | if err := ctx.srcFilter.AddFrameWithFlags(ctx.decFrame, flags); err != nil { 276 | log.Fatalf("Failed to add frame to filter graph: %v\n", err) 277 | } 278 | if err := ctx.filterGraph.RequestOldest(); err != nil { 279 | if err.(*avutil.Error).Code() != avutil.ErrorCodeEOF { 280 | log.Fatalf("Failed to request frame from filter graph: %v\n", err) 281 | } 282 | } 283 | } 284 | 285 | func encodeStream(ctx *context) bool { 286 | if ok := pullFrame(ctx); !ok { 287 | return false 288 | } 289 | defer ctx.encFrame.Unref() 290 | defer ctx.encPkt.Unref() 291 | if ok := encodeFrame(ctx); !ok { 292 | return false 293 | } 294 | ctx.encPkt.SetPosition(-1) 295 | ctx.encPkt.SetStreamIndex(ctx.encStream.Index()) 296 | ctx.encPkt.RescaleTime(ctx.encCodec.TimeBase(), ctx.encStream.TimeBase()) 297 | if err := ctx.encFmt.InterleavedWriteFrame(ctx.encPkt); err != nil { 298 | log.Fatalf("Failed to write packet: %v\n", err) 299 | } 300 | return true 301 | } 302 | 303 | func pullFrame(ctx *context) bool { 304 | ok, err := ctx.sinkFilter.GetFrame(ctx.encFrame) 305 | if err != nil { 306 | log.Fatalf("Failed to get frame from filter graph: %v\n", err) 307 | } 308 | if ok { 309 | ctx.encFrame.SetPictureType(avutil.PictureTypeNone) 310 | } 311 | return ok 312 | } 313 | 314 | func encodeFrame(ctx *context) bool { 315 | ok, err := ctx.encCodec.EncodeVideo(ctx.encPkt, ctx.encFrame) 316 | if err != nil { 317 | log.Fatalf("Failed to encode frame: %v\n", err) 318 | } 319 | return ok 320 | } 321 | 322 | func addFilter(ctx *context, name, id string) *avfilter.Context { 323 | filter := avfilter.FindFilterByName(name) 324 | if filter == nil { 325 | log.Fatalf("Could not find %s/%s filter\n", name, id) 326 | } 327 | fctx, err := ctx.filterGraph.AddFilter(filter, id) 328 | if err != nil { 329 | log.Fatalf("Failed to add %s/%s filter: %v\n", name, id, err) 330 | } 331 | return fctx 332 | } 333 | 334 | func writeHeader(ctx *context) { 335 | if err := ctx.encFmt.WriteHeader(nil); err != nil { 336 | log.Fatalf("Failed to write header: %v\n", err) 337 | } 338 | } 339 | 340 | func writeTrailer(ctx *context) { 341 | if err := ctx.encFmt.WriteTrailer(); err != nil { 342 | log.Fatalf("Failed to write trailer: %v\n", err) 343 | } 344 | } 345 | 346 | type context struct { 347 | // decoding 348 | decFmt *avformat.Context 349 | decStream *avformat.Stream 350 | decCodec *avcodec.Context 351 | decPkt *avcodec.Packet 352 | decFrame *avutil.Frame 353 | srcFilter *avfilter.Context 354 | 355 | // encoding 356 | encFmt *avformat.Context 357 | encStream *avformat.Stream 358 | encCodec *avcodec.Context 359 | encIO *avformat.IOContext 360 | encPkt *avcodec.Packet 361 | encFrame *avutil.Frame 362 | sinkFilter *avfilter.Context 363 | 364 | // transcoding 365 | filterGraph *avfilter.Graph 366 | } 367 | 368 | func newContext() (*context, error) { 369 | ctx := &context{} 370 | if err := ctx.alloc(); err != nil { 371 | ctx.free() 372 | return nil, err 373 | } 374 | return ctx, nil 375 | } 376 | 377 | func (ctx *context) alloc() error { 378 | var err error 379 | if ctx.decPkt, err = avcodec.NewPacket(); err != nil { 380 | return err 381 | } 382 | if ctx.decFrame, err = avutil.NewFrame(); err != nil { 383 | return err 384 | } 385 | if ctx.encPkt, err = avcodec.NewPacket(); err != nil { 386 | return err 387 | } 388 | if ctx.encFrame, err = avutil.NewFrame(); err != nil { 389 | return err 390 | } 391 | if ctx.filterGraph, err = avfilter.NewGraph(); err != nil { 392 | return err 393 | } 394 | return nil 395 | } 396 | 397 | func (ctx *context) free() { 398 | if ctx.encIO != nil { 399 | ctx.encIO.Close() 400 | ctx.encIO = nil 401 | } 402 | if ctx.encFmt != nil { 403 | ctx.encFmt.Free() 404 | ctx.encFmt = nil 405 | } 406 | if ctx.filterGraph != nil { 407 | ctx.filterGraph.Free() 408 | ctx.filterGraph = nil 409 | } 410 | if ctx.encPkt != nil { 411 | ctx.encPkt.Free() 412 | ctx.encPkt = nil 413 | } 414 | if ctx.encFrame != nil { 415 | ctx.encFrame.Free() 416 | ctx.encFrame = nil 417 | } 418 | if ctx.decPkt != nil { 419 | ctx.decPkt.Free() 420 | ctx.decPkt = nil 421 | } 422 | if ctx.decFrame != nil { 423 | ctx.decFrame.Free() 424 | ctx.decFrame = nil 425 | } 426 | if ctx.decCodec != nil { 427 | ctx.decCodec.Free() 428 | ctx.decCodec = nil 429 | } 430 | if ctx.decFmt != nil { 431 | ctx.decFmt.CloseInput() 432 | ctx.decFmt.Free() 433 | ctx.decFmt = nil 434 | } 435 | } 436 | --------------------------------------------------------------------------------