remove local go astiav impl

This commit is contained in:
Leandro Moreira
2024-05-14 15:15:35 -03:00
parent 7ec44e561a
commit f2e9ea7991
129 changed files with 6 additions and 9180 deletions

View File

@@ -20,8 +20,6 @@ RUN apt-get clean && apt-get update && \
ENV GOPROXY=direct ENV GOPROXY=direct
COPY . ./donut COPY . ./donut
# TODO: To remove this hack once the upstream gets merged
COPY ./local-tmp-go-astiav// /Users/leandro.moreira/src/go-astiav/
WORKDIR ${WD}/donut WORKDIR ${WD}/donut
RUN go build . RUN go build .
CMD ["/usr/src/app/donut/donut", "--enable-ice-mux=true"] CMD ["/usr/src/app/donut/donut", "--enable-ice-mux=true"]

5
go.mod generated
View File

@@ -2,11 +2,8 @@ module github.com/flavioribeiro/donut
go 1.19 go 1.19
// TODO: To remove this hack once the upstream gets merged
replace github.com/asticode/go-astiav => /Users/leandro.moreira/src/go-astiav
require ( require (
github.com/asticode/go-astiav v0.12.0 github.com/asticode/go-astiav v0.14.2-0.20240514161420-d8844951c978
github.com/asticode/go-astikit v0.42.0 github.com/asticode/go-astikit v0.42.0
github.com/kelseyhightower/envconfig v1.4.0 github.com/kelseyhightower/envconfig v1.4.0
github.com/pion/webrtc/v3 v3.1.47 github.com/pion/webrtc/v3 v3.1.47

2
go.sum generated
View File

@@ -1,3 +1,5 @@
github.com/asticode/go-astiav v0.14.2-0.20240514161420-d8844951c978 h1:+xACJz51oNEvxrhrHsvGNn16n/vuLmjtvp93LS6onTQ=
github.com/asticode/go-astiav v0.14.2-0.20240514161420-d8844951c978/go.mod h1:K7D8UC6GeQt85FUxk2KVwYxHnotrxuEnp5evkkudc2s=
github.com/asticode/go-astikit v0.42.0 h1:pnir/2KLUSr0527Tv908iAH6EGYYrYta132vvjXsH5w= github.com/asticode/go-astikit v0.42.0 h1:pnir/2KLUSr0527Tv908iAH6EGYYrYta132vvjXsH5w=
github.com/asticode/go-astikit v0.42.0/go.mod h1:h4ly7idim1tNhaVkdVBeXQZEE3L0xblP7fCWbgwipF0= github.com/asticode/go-astikit v0.42.0/go.mod h1:h4ly7idim1tNhaVkdVBeXQZEE3L0xblP7fCWbgwipF0=
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=

View File

@@ -75,7 +75,7 @@ type streamContext struct {
encPkt *astiav.Packet encPkt *astiav.Packet
// Bit stream filter // Bit stream filter
bsfContext *astiav.BSFContext bsfContext *astiav.BitStreamFilterContext
} }
type libAVParams struct { type libAVParams struct {
@@ -540,7 +540,7 @@ func (c *LibAVFFmpegStreamer) prepareBitStreamFilters(p *libAVParams, closer *as
} }
var err error var err error
s.bsfContext, err = astiav.AllocBitStreamContext(bsf) s.bsfContext, err = astiav.AllocBitStreamFilterContext(bsf)
if err != nil { if err != nil {
return fmt.Errorf("error while allocating bit stream context %w", err) return fmt.Errorf("error while allocating bit stream context %w", err)
} }
@@ -551,7 +551,7 @@ func (c *LibAVFFmpegStreamer) prepareBitStreamFilters(p *libAVParams, closer *as
return fmt.Errorf("error while copying codec parameters %w", err) return fmt.Errorf("error while copying codec parameters %w", err)
} }
if err := s.bsfContext.Init(); err != nil { if err := s.bsfContext.Initialize(); err != nil {
return fmt.Errorf("error while initiating %w", err) return fmt.Errorf("error while initiating %w", err)
} }
} }

View File

@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2022 Quentin Renard
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,31 +0,0 @@
version=n5.1.2
srcPath=tmp/$(version)/src
patchPath=
platform=
generate-flags:
go run internal/cmd/flags/main.go
install-ffmpeg:
rm -rf $(srcPath)
mkdir -p $(srcPath)
cd $(srcPath) && git clone https://github.com/FFmpeg/FFmpeg .
cd $(srcPath) && git checkout $(version)
ifneq "" "$(patchPath)"
cd $(srcPath) && git apply $(patchPath)
endif
cd $(srcPath) && ./configure --prefix=.. $(configure)
cd $(srcPath) && make
cd $(srcPath) && make install
coverage:
go test -coverprofile=coverage.out
go tool cover -html=coverage.out
test-platform-build:
docker build -t astiav/$(platform) ./internal/test/$(platform)
test-platform-run:
mkdir -p ./internal/test/$(platform)/tmp/gocache
mkdir -p ./internal/test/$(platform)/tmp/gomodcache
docker run -v .:/opt/astiav -v ./internal/test/$(platform)/tmp/gocache:/opt/gocache -v ./internal/test/$(platform)/tmp/gomodcache:/opt/gomodcache astiav/$(platform)

View File

@@ -1,57 +0,0 @@
[![GoReportCard](http://goreportcard.com/badge/github.com/asticode/go-astiav)](http://goreportcard.com/report/github.com/asticode/go-astiav)
[![GoDoc](https://godoc.org/github.com/asticode/go-astiav?status.svg)](https://godoc.org/github.com/asticode/go-astiav)
[![Test](https://github.com/asticode/go-astiav/actions/workflows/test.yml/badge.svg)](https://github.com/asticode/go-astiav/actions/workflows/test.yml)
[![Coveralls](https://coveralls.io/repos/github/asticode/go-astiav/badge.svg?branch=master)](https://coveralls.io/github/asticode/go-astiav)
`astiav` is a Golang library providing C bindings for [ffmpeg](https://github.com/FFmpeg/FFmpeg)
It's only compatible with `ffmpeg` `n5.1.2`.
Its main goals are to:
- [x] provide a better GO idiomatic API
- standard error pattern
- typed constants and flags
- struct-based functions
- ...
- [x] provide the GO version of [ffmpeg examples](https://github.com/FFmpeg/FFmpeg/tree/n5.1.2/doc/examples)
- [x] be fully tested
# Examples
Examples are located in the [examples](examples) directory and mirror as much as possible the [ffmpeg examples](https://github.com/FFmpeg/FFmpeg/tree/n5.1.2/doc/examples).
|name|astiav|ffmpeg|
|---|---|---|
|Demuxing/Decoding|[see](examples/demuxing_decoding/main.go)|[see](https://github.com/FFmpeg/FFmpeg/blob/n5.1.2/doc/examples/demuxing_decoding.c)
|Filtering|[see](examples/filtering/main.go)|[see](https://github.com/FFmpeg/FFmpeg/blob/n5.1.2/doc/examples/filtering_video.c)
|Hardware Decoding|[see](examples/hardware_decoding/main.go)|[see](https://github.com/FFmpeg/FFmpeg/blob/n5.1.2/doc/examples/hw_decode.c)
|Remuxing|[see](examples/remuxing/main.go)|[see](https://github.com/FFmpeg/FFmpeg/blob/n5.1.2/doc/examples/remuxing.c)
|Scaling|[see](examples/scaling/main.go)|[see](https://github.com/FFmpeg/FFmpeg/blob/n5.1.2/doc/examples/scaling_video.c)
|Transcoding|[see](examples/transcoding/main.go)|[see](https://github.com/FFmpeg/FFmpeg/blob/n5.1.2/doc/examples/transcoding.c)
*Tip: you can use the video sample located in the `testdata` directory for your tests*
# Install ffmpeg from source
If you don't know how to install `ffmpeg`, you can use the following to install it from source:
```sh
$ make install-ffmpeg
```
`ffmpeg` will be built from source in a directory named `tmp` and located in you working directory
For your GO code to pick up `ffmpeg` dependency automatically, you'll need to add the following environment variables:
(don't forget to replace `{{ path to your working directory }}` with the absolute path to your working directory)
```sh
export CGO_LDFLAGS="-L{{ path to your working directory }}/tmp/n5.1.2/lib/",
export CGO_CFLAGS="-I{{ path to your working directory }}/tmp/n5.1.2/include/",
export PKG_CONFIG_PATH="{{ path to your working directory }}/tmp/n5.1.2/lib/pkgconfig",
```
# Why astiav?
After maintaining for several years the most starred [fork](https://github.com/asticode/goav) of [goav](https://github.com/giorgisio/goav), I've decided to write from scratch my own C bindings to fix most of the problems I still encountered using `goav`.

View File

@@ -1,244 +0,0 @@
package astiav
import (
"errors"
"fmt"
"os"
"sync"
"testing"
"github.com/asticode/go-astikit"
)
var globalHelper = newHelper()
func TestMain(m *testing.M) {
// Make sure to exit with the proper code
var code int
defer func(code *int) {
os.Exit(*code)
}(&code)
// Make sure to close global helper
defer globalHelper.close()
// Run
code = m.Run()
}
type helper struct {
closer *astikit.Closer
inputs map[string]*helperInput
m *sync.Mutex // Locks inputs
}
func newHelper() *helper {
return &helper{
closer: astikit.NewCloser(),
inputs: make(map[string]*helperInput),
m: &sync.Mutex{},
}
}
func (h *helper) close() {
h.closer.Close()
}
type helperInput struct {
firstPkt *Packet
formatContext *FormatContext
lastFrame *Frame
}
func (h *helper) inputFormatContext(name string) (fc *FormatContext, err error) {
h.m.Lock()
i, ok := h.inputs[name]
if ok && i.formatContext != nil {
h.m.Unlock()
return i.formatContext, nil
}
h.m.Unlock()
if fc = AllocFormatContext(); fc == nil {
err = errors.New("astiav_test: allocated format context is nil")
return
}
h.closer.Add(fc.Free)
if err = fc.OpenInput("testdata/"+name, nil, nil); err != nil {
err = fmt.Errorf("astiav_test: opening input failed: %w", err)
return
}
h.closer.Add(fc.CloseInput)
if err = fc.FindStreamInfo(nil); err != nil {
err = fmt.Errorf("astiav_test: finding stream info failed: %w", err)
return
}
h.m.Lock()
if _, ok := h.inputs[name]; !ok {
h.inputs[name] = &helperInput{}
}
h.inputs[name].formatContext = fc
h.m.Unlock()
return
}
func (h *helper) inputFirstPacket(name string) (pkt *Packet, err error) {
h.m.Lock()
i, ok := h.inputs[name]
if ok && i.firstPkt != nil {
h.m.Unlock()
return i.firstPkt, nil
}
h.m.Unlock()
var fc *FormatContext
if fc, err = h.inputFormatContext(name); err != nil {
err = fmt.Errorf("astiav_test: getting input format context failed")
return
}
pkt = AllocPacket()
if pkt == nil {
err = errors.New("astiav_test: pkt is nil")
return
}
h.closer.Add(pkt.Free)
if err = fc.ReadFrame(pkt); err != nil {
err = fmt.Errorf("astiav_test: reading frame failed: %w", err)
return
}
h.m.Lock()
h.inputs[name].firstPkt = pkt
h.m.Unlock()
return
}
func (h *helper) inputLastFrame(name string, mediaType MediaType) (f *Frame, err error) {
h.m.Lock()
i, ok := h.inputs[name]
if ok && i.lastFrame != nil {
h.m.Unlock()
return i.lastFrame, nil
}
h.m.Unlock()
var fc *FormatContext
if fc, err = h.inputFormatContext(name); err != nil {
err = fmt.Errorf("astiav_test: getting input format context failed: %w", err)
return
}
var cc *CodecContext
var cs *Stream
for _, s := range fc.Streams() {
if s.CodecParameters().MediaType() != mediaType {
continue
}
cs = s
c := FindDecoder(s.CodecParameters().CodecID())
if c == nil {
err = errors.New("astiav_test: no codec")
return
}
cc = AllocCodecContext(c)
if cc == nil {
err = errors.New("astiav_test: no codec context")
return
}
h.closer.Add(cc.Free)
if err = cs.CodecParameters().ToCodecContext(cc); err != nil {
err = fmt.Errorf("astiav_test: updating codec context failed: %w", err)
return
}
if err = cc.Open(c, nil); err != nil {
err = fmt.Errorf("astiav_test: opening codec context failed: %w", err)
return
}
break
}
if cs == nil {
err = errors.New("astiav_test: no valid video stream")
return
}
var pkt1 *Packet
if pkt1, err = h.inputFirstPacket(name); err != nil {
err = fmt.Errorf("astiav_test: getting input first packet failed: %w", err)
return
}
pkt2 := AllocPacket()
h.closer.Add(pkt2.Free)
f = AllocFrame()
h.closer.Add(f.Free)
lastFrame := AllocFrame()
h.closer.Add(lastFrame.Free)
pkts := []*Packet{pkt1}
for {
if err = fc.ReadFrame(pkt2); err != nil {
if errors.Is(err, ErrEof) || errors.Is(err, ErrEagain) {
if len(pkts) == 0 {
if err = f.Ref(lastFrame); err != nil {
err = fmt.Errorf("astiav_test: last refing frame failed: %w", err)
return
}
err = nil
break
}
} else {
err = fmt.Errorf("astiav_test: reading frame failed: %w", err)
return
}
} else {
pkts = append(pkts, pkt2)
}
for _, pkt := range pkts {
if pkt.StreamIndex() != cs.Index() {
continue
}
if err = cc.SendPacket(pkt); err != nil {
err = fmt.Errorf("astiav_test: sending packet failed: %w", err)
return
}
for {
if err = cc.ReceiveFrame(f); err != nil {
if errors.Is(err, ErrEof) || errors.Is(err, ErrEagain) {
err = nil
break
}
err = fmt.Errorf("astiav_test: receiving frame failed: %w", err)
return
}
if err = lastFrame.Ref(f); err != nil {
err = fmt.Errorf("astiav_test: refing frame failed: %w", err)
return
}
}
}
pkts = []*Packet{}
}
h.m.Lock()
h.inputs[name].lastFrame = f
h.m.Unlock()
return
}

View File

@@ -1,135 +0,0 @@
package astiav
//#cgo pkg-config: libavcodec
//#include <libavcodec/bsf.h>
//#include <stdlib.h>
import "C"
import (
"errors"
"unsafe"
)
// https://github.com/FFmpeg/FFmpeg/blob/release/5.1/libavcodec/bsf.h#L68
type BSFContext struct {
c *C.struct_AVBSFContext
}
func newBSFContextFromC(c *C.struct_AVBSFContext) *BSFContext {
if c == nil {
return nil
}
bsfCtx := &BSFContext{c: c}
classers.set(bsfCtx)
return bsfCtx
}
var _ Classer = (*BSFContext)(nil)
func AllocBitStreamContext(f *BitStreamFilter) (*BSFContext, error) {
if f == nil {
return nil, errors.New("astiav: bit stream filter must not be nil")
}
var bsfCtx *C.struct_AVBSFContext
if err := newError(C.av_bsf_alloc(f.c, &bsfCtx)); err != nil {
return nil, err
}
return newBSFContextFromC(bsfCtx), nil
}
func (bsfCtx *BSFContext) Class() *Class {
return newClassFromC(unsafe.Pointer(bsfCtx.c))
}
func (bsfCtx *BSFContext) Init() error {
return newError(C.av_bsf_init(bsfCtx.c))
}
func (bsfCtx *BSFContext) SendPacket(p *Packet) error {
if p == nil {
return errors.New("astiav: packet must not be nil")
}
return newError(C.av_bsf_send_packet(bsfCtx.c, p.c))
}
func (bsfCtx *BSFContext) ReceivePacket(p *Packet) error {
if p == nil {
return errors.New("astiav: packet must not be nil")
}
return newError(C.av_bsf_receive_packet(bsfCtx.c, p.c))
}
func (bsfCtx *BSFContext) Free() {
classers.del(bsfCtx)
C.av_bsf_free(&bsfCtx.c)
}
func (bsfCtx *BSFContext) TimeBaseIn() Rational {
return newRationalFromC(bsfCtx.c.time_base_in)
}
func (bsfCtx *BSFContext) SetTimeBaseIn(r Rational) {
bsfCtx.c.time_base_in = r.c
}
func (bsfCtx *BSFContext) TimeBaseOut() Rational {
return newRationalFromC(bsfCtx.c.time_base_out)
}
func (bsfCtx *BSFContext) SetTimeBaseOut(r Rational) {
bsfCtx.c.time_base_out = r.c
}
func (bsfCtx *BSFContext) CodecParametersIn() *CodecParameters {
return newCodecParametersFromC(bsfCtx.c.par_in)
}
func (bsfCtx *BSFContext) SetCodecParametersIn(cp *CodecParameters) {
bsfCtx.c.par_in = cp.c
}
func (bsfCtx *BSFContext) CodecParametersOut() *CodecParameters {
return newCodecParametersFromC(bsfCtx.c.par_out)
}
func (bsfCtx *BSFContext) SetCodecParametersOut(cp *CodecParameters) {
bsfCtx.c.par_out = cp.c
}
// https://github.com/FFmpeg/FFmpeg/blob/release/5.1/libavcodec/bsf.h#L111
type BitStreamFilter struct {
c *C.struct_AVBitStreamFilter
}
func newBitStreamFilterFromC(c *C.struct_AVBitStreamFilter) *BitStreamFilter {
if c == nil {
return nil
}
cc := &BitStreamFilter{c: c}
classers.set(cc)
return cc
}
func FindBitStreamFilterByName(n string) *BitStreamFilter {
cn := C.CString(n)
defer C.free(unsafe.Pointer(cn))
return newBitStreamFilterFromC(C.av_bsf_get_by_name(cn))
}
func (bsf *BitStreamFilter) Class() *Class {
return newClassFromC(unsafe.Pointer(bsf.c))
}
func (bsf *BitStreamFilter) Name() string {
return C.GoString(bsf.c.name)
}
// TODO: learn how to work with c arrays
// func (bsf *BitStreamFilter) Codecs() (cids []*CodecID) {
// scs := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((C.enum_AVCodecID)(nil))](C.enum_AVCodecID))(unsafe.Pointer(bsf.c.codec_ids))
// for i := 0; i < fc.NbStreams(); i++ {
// cids = append(cids, CodecID(scs[i]))
// }
// return cids
// }

View File

@@ -1,13 +0,0 @@
package astiav
//#cgo pkg-config: libavfilter
//#include <libavfilter/buffersink.h>
import "C"
type BuffersinkFlag int64
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavfilter/buffersink.h#L89
const (
BuffersinkFlagPeek = BuffersinkFlag(C.AV_BUFFERSINK_FLAG_PEEK)
BuffersinkFlagNoRequest = BuffersinkFlag(C.AV_BUFFERSINK_FLAG_NO_REQUEST)
)

View File

@@ -1,14 +0,0 @@
package astiav
//#cgo pkg-config: libavfilter
//#include <libavfilter/buffersrc.h>
import "C"
type BuffersrcFlag int64
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavfilter/buffersrc.h#L36
const (
BuffersrcFlagNoCheckFormat = BuffersrcFlag(C.AV_BUFFERSRC_FLAG_NO_CHECK_FORMAT)
BuffersrcFlagPush = BuffersrcFlag(C.AV_BUFFERSRC_FLAG_PUSH)
BuffersrcFlagKeepRef = BuffersrcFlag(C.AV_BUFFERSRC_FLAG_KEEP_REF)
)

View File

@@ -1,61 +0,0 @@
package astiav
//#cgo pkg-config: libavcodec
//#include <libavcodec/avcodec.h>
//#include <stdlib.h>
//#include <stdint.h>
import "C"
import (
"errors"
"fmt"
"unsafe"
)
func stringFromC(len int, fn func(buf *C.char, size C.size_t) error) (string, error) {
size := C.size_t(len)
buf := (*C.char)(C.malloc(size))
if buf == nil {
return "", errors.New("astiav: buf is nil")
}
defer C.free(unsafe.Pointer(buf))
if err := fn(buf, size); err != nil {
return "", err
}
return C.GoString(buf), nil
}
func bytesFromC(fn func(size *C.size_t) *C.uint8_t) []byte {
var size uint64
r := fn((*C.size_t)(unsafe.Pointer(&size)))
return C.GoBytes(unsafe.Pointer(r), C.int(size))
}
func bytesToC(b []byte, fn func(b *C.uint8_t, size C.size_t) error) error {
var ptr *C.uint8_t
if b != nil {
c := make([]byte, len(b))
copy(c, b)
ptr = (*C.uint8_t)(unsafe.Pointer(&c[0]))
}
return fn(ptr, C.size_t(len(b)))
}
// TODO Rename?
func setBytesWithIntSizeInC(b []byte, d **C.uint8_t, size *C.int) error {
if len(b) == 0 {
return nil
}
if *d != nil {
C.av_freep(unsafe.Pointer(d))
*size = 0
}
if *d = (*C.uint8_t)(C.av_mallocz(C.size_t(len(b) + C.AV_INPUT_BUFFER_PADDING_SIZE))); *d == nil {
return fmt.Errorf("astiav: allocation is nil")
}
C.memcpy(unsafe.Pointer(*d), unsafe.Pointer(&b[0]), C.size_t(len(b)))
*size = C.int(len(b))
return nil
}

View File

@@ -1,9 +0,0 @@
package astiav
import "testing"
func TestBytes(t *testing.T) {
// TODO Test stringFromC
// TODO Test bytesFromC
// TODO Test bytesToC
}

View File

@@ -1,129 +0,0 @@
package astiav
//#cgo pkg-config: libavutil
//#include <libavutil/channel_layout.h>
/*
// Calling C.AV_CHANNEL_LAYOUT_* in Go gives a "could not determine kind of name for X" error
// therefore we need to bridge the channel layout values
AVChannelLayout *c2goChannelLayoutMono = &(AVChannelLayout)AV_CHANNEL_LAYOUT_MONO;
AVChannelLayout *c2goChannelLayoutStereo = &(AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO;
AVChannelLayout *c2goChannelLayout2Point1 = &(AVChannelLayout)AV_CHANNEL_LAYOUT_2POINT1;
AVChannelLayout *c2goChannelLayout21 = &(AVChannelLayout)AV_CHANNEL_LAYOUT_2_1;
AVChannelLayout *c2goChannelLayoutSurround = &(AVChannelLayout)AV_CHANNEL_LAYOUT_SURROUND;
AVChannelLayout *c2goChannelLayout3Point1 = &(AVChannelLayout)AV_CHANNEL_LAYOUT_3POINT1;
AVChannelLayout *c2goChannelLayout4Point0 = &(AVChannelLayout)AV_CHANNEL_LAYOUT_4POINT0;
AVChannelLayout *c2goChannelLayout4Point1 = &(AVChannelLayout)AV_CHANNEL_LAYOUT_4POINT1;
AVChannelLayout *c2goChannelLayout22 = &(AVChannelLayout)AV_CHANNEL_LAYOUT_2_2;
AVChannelLayout *c2goChannelLayoutQuad = &(AVChannelLayout)AV_CHANNEL_LAYOUT_QUAD;
AVChannelLayout *c2goChannelLayout5Point0 = &(AVChannelLayout)AV_CHANNEL_LAYOUT_5POINT0;
AVChannelLayout *c2goChannelLayout5Point1 = &(AVChannelLayout)AV_CHANNEL_LAYOUT_5POINT1;
AVChannelLayout *c2goChannelLayout5Point0Back = &(AVChannelLayout)AV_CHANNEL_LAYOUT_5POINT0_BACK;
AVChannelLayout *c2goChannelLayout5Point1Back = &(AVChannelLayout)AV_CHANNEL_LAYOUT_5POINT1_BACK;
AVChannelLayout *c2goChannelLayout6Point0 = &(AVChannelLayout)AV_CHANNEL_LAYOUT_6POINT0;
AVChannelLayout *c2goChannelLayout6Point0Front = &(AVChannelLayout)AV_CHANNEL_LAYOUT_6POINT0_FRONT;
AVChannelLayout *c2goChannelLayoutHexagonal = &(AVChannelLayout)AV_CHANNEL_LAYOUT_HEXAGONAL;
AVChannelLayout *c2goChannelLayout6Point1 = &(AVChannelLayout)AV_CHANNEL_LAYOUT_6POINT1;
AVChannelLayout *c2goChannelLayout6Point1Back = &(AVChannelLayout)AV_CHANNEL_LAYOUT_6POINT1_BACK;
AVChannelLayout *c2goChannelLayout6Point1Front = &(AVChannelLayout)AV_CHANNEL_LAYOUT_6POINT1_FRONT;
AVChannelLayout *c2goChannelLayout7Point0 = &(AVChannelLayout)AV_CHANNEL_LAYOUT_7POINT0;
AVChannelLayout *c2goChannelLayout7Point0Front = &(AVChannelLayout)AV_CHANNEL_LAYOUT_7POINT0_FRONT;
AVChannelLayout *c2goChannelLayout7Point1 = &(AVChannelLayout)AV_CHANNEL_LAYOUT_7POINT1;
AVChannelLayout *c2goChannelLayout7Point1Wide = &(AVChannelLayout)AV_CHANNEL_LAYOUT_7POINT1_WIDE;
AVChannelLayout *c2goChannelLayout7Point1WideBack = &(AVChannelLayout)AV_CHANNEL_LAYOUT_7POINT1_WIDE_BACK;
AVChannelLayout *c2goChannelLayoutOctagonal = &(AVChannelLayout)AV_CHANNEL_LAYOUT_OCTAGONAL;
AVChannelLayout *c2goChannelLayoutHexadecagonal = &(AVChannelLayout)AV_CHANNEL_LAYOUT_HEXADECAGONAL;
AVChannelLayout *c2goChannelLayoutStereoDownmix = &(AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO_DOWNMIX;
*/
import "C"
import "unsafe"
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavutil/channel_layout.h#L90
var (
ChannelLayoutMono = newChannelLayoutFromC(C.c2goChannelLayoutMono)
ChannelLayoutStereo = newChannelLayoutFromC(C.c2goChannelLayoutStereo)
ChannelLayout2Point1 = newChannelLayoutFromC(C.c2goChannelLayout2Point1)
ChannelLayout21 = newChannelLayoutFromC(C.c2goChannelLayout21)
ChannelLayoutSurround = newChannelLayoutFromC(C.c2goChannelLayoutSurround)
ChannelLayout3Point1 = newChannelLayoutFromC(C.c2goChannelLayout3Point1)
ChannelLayout4Point0 = newChannelLayoutFromC(C.c2goChannelLayout4Point0)
ChannelLayout4Point1 = newChannelLayoutFromC(C.c2goChannelLayout4Point1)
ChannelLayout22 = newChannelLayoutFromC(C.c2goChannelLayout22)
ChannelLayoutQuad = newChannelLayoutFromC(C.c2goChannelLayoutQuad)
ChannelLayout5Point0 = newChannelLayoutFromC(C.c2goChannelLayout5Point0)
ChannelLayout5Point1 = newChannelLayoutFromC(C.c2goChannelLayout5Point1)
ChannelLayout5Point0Back = newChannelLayoutFromC(C.c2goChannelLayout5Point0Back)
ChannelLayout5Point1Back = newChannelLayoutFromC(C.c2goChannelLayout5Point1Back)
ChannelLayout6Point0 = newChannelLayoutFromC(C.c2goChannelLayout6Point0)
ChannelLayout6Point0Front = newChannelLayoutFromC(C.c2goChannelLayout6Point0Front)
ChannelLayoutHexagonal = newChannelLayoutFromC(C.c2goChannelLayoutHexagonal)
ChannelLayout6Point1 = newChannelLayoutFromC(C.c2goChannelLayout6Point1)
ChannelLayout6Point1Back = newChannelLayoutFromC(C.c2goChannelLayout6Point1Back)
ChannelLayout6Point1Front = newChannelLayoutFromC(C.c2goChannelLayout6Point1Front)
ChannelLayout7Point0 = newChannelLayoutFromC(C.c2goChannelLayout7Point0)
ChannelLayout7Point0Front = newChannelLayoutFromC(C.c2goChannelLayout7Point0Front)
ChannelLayout7Point1 = newChannelLayoutFromC(C.c2goChannelLayout7Point1)
ChannelLayout7Point1Wide = newChannelLayoutFromC(C.c2goChannelLayout7Point1Wide)
ChannelLayout7Point1WideBack = newChannelLayoutFromC(C.c2goChannelLayout7Point1WideBack)
ChannelLayoutOctagonal = newChannelLayoutFromC(C.c2goChannelLayoutOctagonal)
ChannelLayoutHexadecagonal = newChannelLayoutFromC(C.c2goChannelLayoutHexadecagonal)
ChannelLayoutStereoDownmix = newChannelLayoutFromC(C.c2goChannelLayoutStereoDownmix)
)
type ChannelLayout struct {
c *C.struct_AVChannelLayout
}
func newChannelLayoutFromC(c *C.struct_AVChannelLayout) ChannelLayout {
return ChannelLayout{c: c}
}
func (l ChannelLayout) NbChannels() int {
return int(l.c.nb_channels)
}
func (l ChannelLayout) String() string {
b := make([]byte, 1024)
n, err := l.Describe(b)
if err != nil {
return ""
}
return string(b[:n])
}
func (l ChannelLayout) Describe(b []byte) (int, error) {
ret := C.av_channel_layout_describe(l.c, (*C.char)(unsafe.Pointer(&b[0])), C.size_t(len(b)))
if ret < 0 {
return 0, newError(ret)
}
return int(ret), nil
}
func (l ChannelLayout) Valid() bool {
return C.av_channel_layout_check(l.c) > 0
}
func (l ChannelLayout) Compare(l2 ChannelLayout) (equal bool, err error) {
ret := C.av_channel_layout_compare(l.c, l2.c)
if ret < 0 {
return false, newError(ret)
}
return ret == 0, nil
}
func (l ChannelLayout) Equal(l2 ChannelLayout) bool {
v, _ := l.Compare(l2)
return v
}
func (l ChannelLayout) copy(dst *C.struct_AVChannelLayout) error {
return newError(C.av_channel_layout_copy(dst, l.c))
}
func (l ChannelLayout) clone() (ChannelLayout, error) {
var cl C.struct_AVChannelLayout
err := l.copy(&cl)
dst := newChannelLayoutFromC(&cl)
return dst, err
}

View File

@@ -1,16 +0,0 @@
package astiav
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestChannelLayout(t *testing.T) {
cl := ChannelLayoutStereo
require.Equal(t, 2, cl.NbChannels())
require.Equal(t, "stereo", cl.String())
require.True(t, cl.Valid())
require.True(t, cl.Equal(ChannelLayoutStereo))
require.False(t, cl.Equal(ChannelLayoutMono))
}

View File

@@ -1,19 +0,0 @@
package astiav
//#cgo pkg-config: libavutil
//#include <libavutil/pixfmt.h>
import "C"
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavutil/pixfmt.h#L616
type ChromaLocation C.enum_AVChromaLocation
const (
ChromaLocationUnspecified = ChromaLocation(C.AVCHROMA_LOC_UNSPECIFIED)
ChromaLocationLeft = ChromaLocation(C.AVCHROMA_LOC_LEFT)
ChromaLocationCenter = ChromaLocation(C.AVCHROMA_LOC_CENTER)
ChromaLocationTopleft = ChromaLocation(C.AVCHROMA_LOC_TOPLEFT)
ChromaLocationTop = ChromaLocation(C.AVCHROMA_LOC_TOP)
ChromaLocationBottomleft = ChromaLocation(C.AVCHROMA_LOC_BOTTOMLEFT)
ChromaLocationBottom = ChromaLocation(C.AVCHROMA_LOC_BOTTOM)
ChromaLocationNb = ChromaLocation(C.AVCHROMA_LOC_NB)
)

View File

@@ -1,134 +0,0 @@
package astiav
//#cgo pkg-config: libavutil
//#include <libavutil/log.h>
//#include <stdint.h>
/*
static inline char* astiavClassItemName(AVClass* c, void* ptr) {
return (char*)c->item_name(ptr);
}
static inline AVClassCategory astiavClassCategory(AVClass* c, void* ptr) {
if (c->get_category) return c->get_category(ptr);
return c->category;
}
static inline AVClass** astiavClassParent(AVClass* c, void* ptr) {
if (c->parent_log_context_offset) {
AVClass** parent = *(AVClass ***) (((uint8_t *) ptr) + c->parent_log_context_offset);
if (parent && *parent) {
return parent;
}
}
return NULL;
}
*/
import "C"
import (
"fmt"
"sync"
"unsafe"
)
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavutil/log.h#L66
type Class struct {
c *C.struct_AVClass
ptr unsafe.Pointer
}
func newClassFromC(ptr unsafe.Pointer) *Class {
if ptr == nil {
return nil
}
c := (**C.struct_AVClass)(ptr)
if c == nil {
return nil
}
return &Class{
c: *c,
ptr: ptr,
}
}
func (c *Class) Category() ClassCategory {
return ClassCategory(C.astiavClassCategory(c.c, c.ptr))
}
func (c *Class) ItemName() string {
return C.GoString(C.astiavClassItemName(c.c, c.ptr))
}
func (c *Class) Name() string {
return C.GoString(c.c.class_name)
}
func (c *Class) Parent() *Class {
return newClassFromC(unsafe.Pointer(C.astiavClassParent(c.c, c.ptr)))
}
func (c *Class) String() string {
return fmt.Sprintf("%s [%s] @ %p", c.ItemName(), c.Name(), c.ptr)
}
type Classer interface {
Class() *Class
}
type UnknownClasser struct {
c *Class
}
func newUnknownClasser(ptr unsafe.Pointer) *UnknownClasser {
return &UnknownClasser{c: newClassFromC(ptr)}
}
func (c *UnknownClasser) Class() *Class {
return c.c
}
var classers = newClasserPool()
type classerPool struct {
m sync.Mutex
p map[unsafe.Pointer]Classer
}
func newClasserPool() *classerPool {
return &classerPool{p: make(map[unsafe.Pointer]Classer)}
}
func (p *classerPool) unsafePointer(c Classer) unsafe.Pointer {
if c == nil {
return nil
}
cl := c.Class()
if cl == nil {
return nil
}
return cl.ptr
}
func (p *classerPool) set(c Classer) {
p.m.Lock()
defer p.m.Unlock()
if ptr := p.unsafePointer(c); ptr != nil {
p.p[ptr] = c
}
}
func (p *classerPool) del(c Classer) {
p.m.Lock()
defer p.m.Unlock()
if ptr := p.unsafePointer(c); ptr != nil {
delete(p.p, ptr)
}
}
func (p *classerPool) get(ptr unsafe.Pointer) (Classer, bool) {
p.m.Lock()
defer p.m.Unlock()
c, ok := p.p[ptr]
return c, ok
}

View File

@@ -1,30 +0,0 @@
package astiav
//#cgo pkg-config: libavutil
//#include <libavutil/log.h>
import "C"
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavutil/log.h#L28
// TODO Find a way to use C.enum_AVClassCategory instead of uint
type ClassCategory uint
const (
ClassCategoryBitstreamFilter = ClassCategory(C.AV_CLASS_CATEGORY_BITSTREAM_FILTER)
ClassCategoryDecoder = ClassCategory(C.AV_CLASS_CATEGORY_DECODER)
ClassCategoryDemuxer = ClassCategory(C.AV_CLASS_CATEGORY_DEMUXER)
ClassCategoryDeviceAudioInput = ClassCategory(C.AV_CLASS_CATEGORY_DEVICE_AUDIO_INPUT)
ClassCategoryDeviceAudioOutput = ClassCategory(C.AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT)
ClassCategoryDeviceInput = ClassCategory(C.AV_CLASS_CATEGORY_DEVICE_INPUT)
ClassCategoryDeviceOutput = ClassCategory(C.AV_CLASS_CATEGORY_DEVICE_OUTPUT)
ClassCategoryDeviceVideoInput = ClassCategory(C.AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT)
ClassCategoryDeviceVideoOutput = ClassCategory(C.AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT)
ClassCategoryEncoder = ClassCategory(C.AV_CLASS_CATEGORY_ENCODER)
ClassCategoryFilter = ClassCategory(C.AV_CLASS_CATEGORY_FILTER)
ClassCategoryInput = ClassCategory(C.AV_CLASS_CATEGORY_INPUT)
ClassCategoryMuxer = ClassCategory(C.AV_CLASS_CATEGORY_MUXER)
ClassCategoryNa = ClassCategory(C.AV_CLASS_CATEGORY_NA)
ClassCategoryNb = ClassCategory(C.AV_CLASS_CATEGORY_NB)
ClassCategoryOutput = ClassCategory(C.AV_CLASS_CATEGORY_OUTPUT)
ClassCategorySwresampler = ClassCategory(C.AV_CLASS_CATEGORY_SWRESAMPLER)
ClassCategorySwscaler = ClassCategory(C.AV_CLASS_CATEGORY_SWSCALER)
)

View File

@@ -1,63 +0,0 @@
package astiav
import (
"fmt"
"os"
"path/filepath"
"testing"
"unsafe"
"github.com/stretchr/testify/require"
)
func TestClass(t *testing.T) {
c := FindDecoder(CodecIDMjpeg)
require.NotNil(t, c)
cc := AllocCodecContext(c)
require.NotNil(t, cc)
defer cc.Free()
cl := cc.Class()
require.NotNil(t, cl)
require.Equal(t, ClassCategoryDecoder, cl.Category())
require.Equal(t, "mjpeg", cl.ItemName())
require.Equal(t, "AVCodecContext", cl.Name())
require.Equal(t, fmt.Sprintf("mjpeg [AVCodecContext] @ %p", cc.c), cl.String())
// TODO Test parent
}
func TestClassers(t *testing.T) {
cl := len(classers.p)
f := AllocFilterGraph()
c := FindDecoder(CodecIDMjpeg)
require.NotNil(t, c)
cc := AllocCodecContext(c)
require.NotNil(t, cc)
bufferSink := FindFilterByName("buffersink")
require.NotNil(t, bufferSink)
fc, err := f.NewFilterContext(bufferSink, "filter_out", nil)
require.NoError(t, err)
fmc1 := AllocFormatContext()
fmc2 := AllocFormatContext()
require.NoError(t, fmc2.OpenInput("testdata/video.mp4", nil, nil))
path := filepath.Join(t.TempDir(), "iocontext.txt")
ic, err := OpenIOContext(path, NewIOContextFlags(IOContextFlagWrite))
require.NoError(t, err)
defer os.RemoveAll(path)
ssc, err := CreateSoftwareScaleContext(1, 1, PixelFormatRgba, 2, 2, PixelFormatRgba, NewSoftwareScaleContextFlags())
require.NoError(t, err)
require.Equal(t, cl+8, len(classers.p))
v, ok := classers.get(unsafe.Pointer(f.c))
require.True(t, ok)
require.Equal(t, f, v)
cc.Free()
fc.Free()
f.Free()
fmc1.Free()
fmc2.CloseInput()
require.NoError(t, ic.Close())
ssc.Free()
require.Equal(t, cl, len(classers.p))
}

View File

@@ -1,131 +0,0 @@
package astiav
//#cgo pkg-config: libavcodec libavutil
//#include <libavcodec/avcodec.h>
//#include <libavutil/channel_layout.h>
import "C"
import (
"unsafe"
)
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavcodec/codec.h#L202
type Codec struct {
c *C.struct_AVCodec
}
func newCodecFromC(c *C.struct_AVCodec) *Codec {
if c == nil {
return nil
}
return &Codec{c: c}
}
func (c *Codec) Name() string {
return C.GoString(c.c.name)
}
func (c *Codec) String() string {
return c.Name()
}
func (c *Codec) ID() CodecID {
return CodecID(c.c.id)
}
func (c *Codec) ChannelLayouts() (o []ChannelLayout) {
if c.c.ch_layouts == nil {
return nil
}
size := unsafe.Sizeof(*c.c.ch_layouts)
for i := 0; ; i++ {
v, _ := newChannelLayoutFromC((*C.struct_AVChannelLayout)(unsafe.Pointer(uintptr(unsafe.Pointer(c.c.ch_layouts)) + uintptr(i)*size))).clone()
if !v.Valid() {
break
}
o = append(o, v)
}
return
}
func (c *Codec) IsDecoder() bool {
return int(C.av_codec_is_decoder(c.c)) != 0
}
func (c *Codec) IsEncoder() bool {
return int(C.av_codec_is_encoder(c.c)) != 0
}
func (c *Codec) PixelFormats() (o []PixelFormat) {
if c.c.pix_fmts == nil {
return nil
}
size := unsafe.Sizeof(*c.c.pix_fmts)
for i := 0; ; i++ {
p := *(*C.int)(unsafe.Pointer(uintptr(unsafe.Pointer(c.c.pix_fmts)) + uintptr(i)*size))
if p == C.AV_PIX_FMT_NONE {
break
}
o = append(o, PixelFormat(p))
}
return
}
func (c *Codec) SampleFormats() (o []SampleFormat) {
if c.c.sample_fmts == nil {
return nil
}
size := unsafe.Sizeof(*c.c.sample_fmts)
for i := 0; ; i++ {
p := *(*C.int)(unsafe.Pointer(uintptr(unsafe.Pointer(c.c.sample_fmts)) + uintptr(i)*size))
if p == C.AV_SAMPLE_FMT_NONE {
break
}
o = append(o, SampleFormat(p))
}
return
}
func FindDecoder(id CodecID) *Codec {
return newCodecFromC(C.avcodec_find_decoder((C.enum_AVCodecID)(id)))
}
func FindDecoderByName(n string) *Codec {
cn := C.CString(n)
defer C.free(unsafe.Pointer(cn))
return newCodecFromC(C.avcodec_find_decoder_by_name(cn))
}
func FindEncoder(id CodecID) *Codec {
return newCodecFromC(C.avcodec_find_encoder((C.enum_AVCodecID)(id)))
}
func FindEncoderByName(n string) *Codec {
cn := C.CString(n)
defer C.free(unsafe.Pointer(cn))
return newCodecFromC(C.avcodec_find_encoder_by_name(cn))
}
func (c *Codec) HardwareConfigs() (configs []CodecHardwareConfig) {
var i int
for {
config := C.avcodec_get_hw_config(c.c, C.int(i))
if config == nil {
break
}
configs = append(configs, newCodecHardwareConfigFromC(config))
i++
}
return
}
func Codecs() (cs []*Codec) {
var opq *C.void = nil
for {
c := C.av_codec_iterate((*unsafe.Pointer)(unsafe.Pointer(&opq)))
if c == nil {
break
}
cs = append(cs, newCodecFromC(c))
}
return
}

View File

@@ -1,395 +0,0 @@
package astiav
//#cgo pkg-config: libavcodec libavutil
//#include <libavcodec/avcodec.h>
//#include <libavutil/frame.h>
/*
extern enum AVPixelFormat goAstiavCodecContextGetFormat(AVCodecContext *ctx, enum AVPixelFormat *pix_fmts, int pix_fmts_size);
static inline enum AVPixelFormat astiavCodecContextGetFormat(AVCodecContext *ctx, const enum AVPixelFormat *pix_fmts)
{
int pix_fmts_size = 0;
while (*pix_fmts != AV_PIX_FMT_NONE) {
pix_fmts_size++;
pix_fmts++;
}
pix_fmts -= pix_fmts_size;
return goAstiavCodecContextGetFormat(ctx, (enum AVPixelFormat*)(pix_fmts), pix_fmts_size);
}
static inline void astiavSetCodecContextGetFormat(AVCodecContext *ctx)
{
ctx->get_format = astiavCodecContextGetFormat;
}
static inline void astiavResetCodecContextGetFormat(AVCodecContext *ctx)
{
ctx->get_format = NULL;
}
*/
import "C"
import (
"sync"
"unsafe"
)
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavcodec/avcodec.h#L383
type CodecContext struct {
c *C.struct_AVCodecContext
// We need to store this to unref it properly
hdc *HardwareDeviceContext
}
func newCodecContextFromC(c *C.struct_AVCodecContext) *CodecContext {
if c == nil {
return nil
}
cc := &CodecContext{c: c}
classers.set(cc)
return cc
}
var _ Classer = (*CodecContext)(nil)
func AllocCodecContext(c *Codec) *CodecContext {
var cc *C.struct_AVCodec
if c != nil {
cc = c.c
}
return newCodecContextFromC(C.avcodec_alloc_context3(cc))
}
func (cc *CodecContext) Free() {
if cc.hdc != nil {
C.av_buffer_unref(&cc.hdc.c)
cc.hdc = nil
}
classers.del(cc)
C.avcodec_free_context(&cc.c)
}
func (cc *CodecContext) String() string {
s, _ := stringFromC(255, func(buf *C.char, size C.size_t) error {
C.avcodec_string(buf, C.int(size), cc.c, C.int(0))
return nil
})
return s
}
func (cc *CodecContext) BitRate() int64 {
return int64(cc.c.bit_rate)
}
func (cc *CodecContext) SetBitRate(bitRate int64) {
cc.c.bit_rate = C.int64_t(bitRate)
}
func (cc *CodecContext) Channels() int {
return int(cc.c.channels)
}
func (cc *CodecContext) SetChannels(channels int) {
cc.c.channels = C.int(channels)
}
func (cc *CodecContext) ChannelLayout() ChannelLayout {
l, _ := newChannelLayoutFromC(&cc.c.ch_layout).clone()
return l
}
func (cc *CodecContext) SetChannelLayout(l ChannelLayout) {
l.copy(&cc.c.ch_layout) //nolint: errcheck
}
func (cc *CodecContext) ChromaLocation() ChromaLocation {
return ChromaLocation(cc.c.chroma_sample_location)
}
func (cc *CodecContext) Class() *Class {
return newClassFromC(unsafe.Pointer(cc.c))
}
func (cc *CodecContext) CodecID() CodecID {
return CodecID(cc.c.codec_id)
}
func (cc *CodecContext) ColorPrimaries() ColorPrimaries {
return ColorPrimaries(cc.c.color_primaries)
}
func (cc *CodecContext) ColorRange() ColorRange {
return ColorRange(cc.c.color_range)
}
func (cc *CodecContext) ColorSpace() ColorSpace {
return ColorSpace(cc.c.colorspace)
}
func (cc *CodecContext) ColorTransferCharacteristic() ColorTransferCharacteristic {
return ColorTransferCharacteristic(cc.c.color_trc)
}
func (cc *CodecContext) ExtraData() []byte {
return bytesFromC(func(size *C.size_t) *C.uint8_t {
*size = C.size_t(cc.c.extradata_size)
return cc.c.extradata
})
}
func (cc *CodecContext) SetExtraData(b []byte) error {
return setBytesWithIntSizeInC(b, &cc.c.extradata, &cc.c.extradata_size)
}
func (cc *CodecContext) Flags() CodecContextFlags {
return CodecContextFlags(cc.c.flags)
}
func (cc *CodecContext) SetFlags(fs CodecContextFlags) {
cc.c.flags = C.int(fs)
}
func (cc *CodecContext) Flags2() CodecContextFlags2 {
return CodecContextFlags2(cc.c.flags2)
}
func (cc *CodecContext) SetFlags2(fs CodecContextFlags2) {
cc.c.flags2 = C.int(fs)
}
func (cc *CodecContext) Framerate() Rational {
return newRationalFromC(cc.c.framerate)
}
func (cc *CodecContext) SetFramerate(f Rational) {
cc.c.framerate = f.c
}
func (cc *CodecContext) FrameSize() int {
return int(cc.c.frame_size)
}
func (cc *CodecContext) GopSize() int {
return int(cc.c.gop_size)
}
func (cc *CodecContext) SetGopSize(gopSize int) {
cc.c.gop_size = C.int(gopSize)
}
func (cc *CodecContext) Height() int {
return int(cc.c.height)
}
func (cc *CodecContext) SetHeight(height int) {
cc.c.height = C.int(height)
}
func (cc *CodecContext) Level() Level {
return Level(cc.c.level)
}
func (cc *CodecContext) SetLevel(l Level) {
cc.c.level = C.int(l)
}
func (cc *CodecContext) MediaType() MediaType {
return MediaType(cc.c.codec_type)
}
func (cc *CodecContext) PixelFormat() PixelFormat {
return PixelFormat(cc.c.pix_fmt)
}
func (cc *CodecContext) SetPixelFormat(pixFmt PixelFormat) {
cc.c.pix_fmt = C.enum_AVPixelFormat(pixFmt)
}
func (cc *CodecContext) Profile() Profile {
return Profile(cc.c.profile)
}
func (cc *CodecContext) SetProfile(p Profile) {
cc.c.profile = C.int(p)
}
func (cc *CodecContext) Qmin() int {
return int(cc.c.qmin)
}
func (cc *CodecContext) SetQmin(qmin int) {
cc.c.qmin = C.int(qmin)
}
func (cc *CodecContext) SampleAspectRatio() Rational {
return newRationalFromC(cc.c.sample_aspect_ratio)
}
func (cc *CodecContext) SetSampleAspectRatio(r Rational) {
cc.c.sample_aspect_ratio = r.c
}
func (cc *CodecContext) SampleFormat() SampleFormat {
return SampleFormat(cc.c.sample_fmt)
}
func (cc *CodecContext) SetSampleFormat(f SampleFormat) {
cc.c.sample_fmt = C.enum_AVSampleFormat(f)
}
func (cc *CodecContext) SampleRate() int {
return int(cc.c.sample_rate)
}
func (cc *CodecContext) SetSampleRate(sampleRate int) {
cc.c.sample_rate = C.int(sampleRate)
}
func (cc *CodecContext) StrictStdCompliance() StrictStdCompliance {
return StrictStdCompliance(cc.c.strict_std_compliance)
}
func (cc *CodecContext) SetStrictStdCompliance(c StrictStdCompliance) {
cc.c.strict_std_compliance = C.int(c)
}
func (cc *CodecContext) TimeBase() Rational {
return newRationalFromC(cc.c.time_base)
}
func (cc *CodecContext) SetTimeBase(r Rational) {
cc.c.time_base = r.c
}
func (cc *CodecContext) ThreadCount() int {
return int(cc.c.thread_count)
}
func (cc *CodecContext) SetThreadCount(threadCount int) {
cc.c.thread_count = C.int(threadCount)
}
func (cc *CodecContext) ThreadType() ThreadType {
return ThreadType(cc.c.thread_type)
}
func (cc *CodecContext) SetThreadType(t ThreadType) {
cc.c.thread_type = C.int(t)
}
func (cc *CodecContext) Width() int {
return int(cc.c.width)
}
func (cc *CodecContext) SetWidth(width int) {
cc.c.width = C.int(width)
}
func (cc *CodecContext) Open(c *Codec, d *Dictionary) error {
var dc **C.struct_AVDictionary
if d != nil {
dc = &d.c
}
return newError(C.avcodec_open2(cc.c, c.c, dc))
}
func (cc *CodecContext) ReceivePacket(p *Packet) error {
var pc *C.struct_AVPacket
if p != nil {
pc = p.c
}
return newError(C.avcodec_receive_packet(cc.c, pc))
}
func (cc *CodecContext) SendPacket(p *Packet) error {
var pc *C.struct_AVPacket
if p != nil {
pc = p.c
}
return newError(C.avcodec_send_packet(cc.c, pc))
}
func (cc *CodecContext) ReceiveFrame(f *Frame) error {
var fc *C.struct_AVFrame
if f != nil {
fc = f.c
}
return newError(C.avcodec_receive_frame(cc.c, fc))
}
func (cc *CodecContext) SendFrame(f *Frame) error {
var fc *C.struct_AVFrame
if f != nil {
fc = f.c
}
return newError(C.avcodec_send_frame(cc.c, fc))
}
func (cc *CodecContext) ToCodecParameters(cp *CodecParameters) error {
return cp.FromCodecContext(cc)
}
func (cc *CodecContext) FromCodecParameters(cp *CodecParameters) error {
return cp.ToCodecContext(cc)
}
func (cc *CodecContext) SetHardwareDeviceContext(hdc *HardwareDeviceContext) {
if cc.hdc != nil {
C.av_buffer_unref(&cc.hdc.c)
}
cc.hdc = hdc
if cc.hdc != nil {
cc.c.hw_device_ctx = C.av_buffer_ref(cc.hdc.c)
}
}
func (cc *CodecContext) ExtraHardwareFrames() int {
return int(cc.c.extra_hw_frames)
}
func (cc *CodecContext) SetExtraHardwareFrames(n int) {
cc.c.extra_hw_frames = C.int(n)
}
type CodecContextPixelFormatCallback func(pfs []PixelFormat) PixelFormat
var (
codecContextPixelFormatCallbacks = make(map[*C.struct_AVCodecContext]CodecContextPixelFormatCallback)
codecContextPixelFormatCallbacksMutex = &sync.Mutex{}
)
func (cc *CodecContext) SetPixelFormatCallback(c CodecContextPixelFormatCallback) {
// Lock
codecContextPixelFormatCallbacksMutex.Lock()
defer codecContextPixelFormatCallbacksMutex.Unlock()
// Update callback
if c == nil {
C.astiavResetCodecContextGetFormat(cc.c)
delete(codecContextPixelFormatCallbacks, cc.c)
} else {
C.astiavSetCodecContextGetFormat(cc.c)
codecContextPixelFormatCallbacks[cc.c] = c
}
}
//export goAstiavCodecContextGetFormat
func goAstiavCodecContextGetFormat(cc *C.struct_AVCodecContext, pfsCPtr *C.enum_AVPixelFormat, pfsCSize C.int) C.enum_AVPixelFormat {
// Lock
codecContextPixelFormatCallbacksMutex.Lock()
defer codecContextPixelFormatCallbacksMutex.Unlock()
// Get callback
c, ok := codecContextPixelFormatCallbacks[cc]
if !ok {
return C.enum_AVPixelFormat(PixelFormatNone)
}
// Get pixel formats
var pfs []PixelFormat
for _, v := range unsafe.Slice(pfsCPtr, pfsCSize) {
pfs = append(pfs, PixelFormat(v))
}
// Callback
return C.enum_AVPixelFormat(c(pfs))
}

View File

@@ -1,45 +0,0 @@
package astiav
//#cgo pkg-config: libavcodec
//#include <libavcodec/avcodec.h>
import "C"
type CodecContextFlag int64
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavcodec/avcodec.h#L206
const (
CodecContextFlagUnaligned = CodecContextFlag(C.AV_CODEC_FLAG_UNALIGNED)
CodecContextFlagQscale = CodecContextFlag(C.AV_CODEC_FLAG_QSCALE)
CodecContextFlag4Mv = CodecContextFlag(C.AV_CODEC_FLAG_4MV)
CodecContextFlagOutputCorrupt = CodecContextFlag(C.AV_CODEC_FLAG_OUTPUT_CORRUPT)
CodecContextFlagQpel = CodecContextFlag(C.AV_CODEC_FLAG_QPEL)
CodecContextFlagPass1 = CodecContextFlag(C.AV_CODEC_FLAG_PASS1)
CodecContextFlagPass2 = CodecContextFlag(C.AV_CODEC_FLAG_PASS2)
CodecContextFlagLoopFilter = CodecContextFlag(C.AV_CODEC_FLAG_LOOP_FILTER)
CodecContextFlagGray = CodecContextFlag(C.AV_CODEC_FLAG_GRAY)
CodecContextFlagPsnr = CodecContextFlag(C.AV_CODEC_FLAG_PSNR)
CodecContextFlagTruncated = CodecContextFlag(C.AV_CODEC_FLAG_TRUNCATED)
CodecContextFlagInterlacedDct = CodecContextFlag(C.AV_CODEC_FLAG_INTERLACED_DCT)
CodecContextFlagLowDelay = CodecContextFlag(C.AV_CODEC_FLAG_LOW_DELAY)
CodecContextFlagGlobalHeader = CodecContextFlag(C.AV_CODEC_FLAG_GLOBAL_HEADER)
CodecContextFlagBitexact = CodecContextFlag(C.AV_CODEC_FLAG_BITEXACT)
CodecContextFlagAcPred = CodecContextFlag(C.AV_CODEC_FLAG_AC_PRED)
CodecContextFlagInterlacedMe = CodecContextFlag(C.AV_CODEC_FLAG_INTERLACED_ME)
CodecContextFlagClosedGop = CodecContextFlag(C.AV_CODEC_FLAG_CLOSED_GOP)
)
type CodecContextFlag2 int64
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavcodec/avcodec.h#L287
const (
CodecFlag2Fast = CodecContextFlag2(C.AV_CODEC_FLAG2_FAST)
CodecFlag2NoOutput = CodecContextFlag2(C.AV_CODEC_FLAG2_NO_OUTPUT)
CodecFlag2LocalHeader = CodecContextFlag2(C.AV_CODEC_FLAG2_LOCAL_HEADER)
CodecFlag2DropFrameTimecode = CodecContextFlag2(C.AV_CODEC_FLAG2_DROP_FRAME_TIMECODE)
CodecFlag2Chunks = CodecContextFlag2(C.AV_CODEC_FLAG2_CHUNKS)
CodecFlag2IgnoreCrop = CodecContextFlag2(C.AV_CODEC_FLAG2_IGNORE_CROP)
CodecFlag2ShowAll = CodecContextFlag2(C.AV_CODEC_FLAG2_SHOW_ALL)
CodecFlag2ExportMvs = CodecContextFlag2(C.AV_CODEC_FLAG2_EXPORT_MVS)
CodecFlag2SkipManual = CodecContextFlag2(C.AV_CODEC_FLAG2_SKIP_MANUAL)
CodecFlag2RoFlushNoop = CodecContextFlag2(C.AV_CODEC_FLAG2_RO_FLUSH_NOOP)
)

View File

@@ -1,149 +0,0 @@
package astiav
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestCodecContext(t *testing.T) {
fc, err := globalHelper.inputFormatContext("video.mp4")
require.NoError(t, err)
ss := fc.Streams()
require.Len(t, ss, 2)
s1 := ss[0]
s2 := ss[1]
c1 := FindDecoder(s1.CodecParameters().CodecID())
require.NotNil(t, c1)
cc1 := AllocCodecContext(c1)
require.NotNil(t, cc1)
defer cc1.Free()
err = s1.CodecParameters().ToCodecContext(cc1)
require.NoError(t, err)
require.Equal(t, "Video: h264 (Constrained Baseline) (avc1 / 0x31637661), yuv420p(progressive), 320x180 [SAR 1:1 DAR 16:9], 441 kb/s", cc1.String())
require.Equal(t, int64(441324), cc1.BitRate())
require.Equal(t, ChromaLocationLeft, cc1.ChromaLocation())
require.Equal(t, CodecIDH264, cc1.CodecID())
require.Equal(t, ColorPrimariesUnspecified, cc1.ColorPrimaries())
require.Equal(t, ColorRangeUnspecified, cc1.ColorRange())
require.Equal(t, ColorSpaceUnspecified, cc1.ColorSpace())
require.Equal(t, ColorTransferCharacteristicUnspecified, cc1.ColorTransferCharacteristic())
require.Equal(t, 12, cc1.GopSize())
require.Equal(t, 180, cc1.Height())
require.Equal(t, Level(13), cc1.Level())
require.Equal(t, MediaTypeVideo, cc1.MediaType())
require.Equal(t, PixelFormatYuv420P, cc1.PixelFormat())
require.Equal(t, ProfileH264ConstrainedBaseline, cc1.Profile())
require.Equal(t, NewRational(1, 1), cc1.SampleAspectRatio())
require.Equal(t, StrictStdComplianceNormal, cc1.StrictStdCompliance())
require.Equal(t, 1, cc1.ThreadCount())
require.Equal(t, ThreadType(3), cc1.ThreadType())
require.Equal(t, 320, cc1.Width())
cl := cc1.Class()
require.NotNil(t, cl)
require.Equal(t, "AVCodecContext", cl.Name())
c2 := FindDecoder(s2.CodecParameters().CodecID())
require.NotNil(t, c2)
cc2 := AllocCodecContext(c2)
require.NotNil(t, cc2)
defer cc2.Free()
err = s2.CodecParameters().ToCodecContext(cc2)
require.NoError(t, err)
require.Equal(t, "Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 161 kb/s", cc2.String())
require.Equal(t, int64(161052), cc2.BitRate())
require.Equal(t, 2, cc2.Channels())
require.True(t, cc2.ChannelLayout().Equal(ChannelLayoutStereo))
require.Equal(t, CodecIDAac, cc2.CodecID())
require.Equal(t, 1024, cc2.FrameSize())
require.Equal(t, MediaTypeAudio, cc2.MediaType())
require.Equal(t, SampleFormatFltp, cc2.SampleFormat())
require.Equal(t, 48000, cc2.SampleRate())
require.Equal(t, StrictStdComplianceNormal, cc2.StrictStdCompliance())
require.Equal(t, 1, cc2.ThreadCount())
require.Equal(t, ThreadType(3), cc2.ThreadType())
c3 := FindEncoder(CodecIDMjpeg)
require.NotNil(t, c3)
cc3 := AllocCodecContext(c3)
require.NotNil(t, cc3)
defer cc3.Free()
cc3.SetHeight(2)
cc3.SetPixelFormat(PixelFormatYuvj420P)
cc3.SetTimeBase(NewRational(1, 1))
cc3.SetWidth(3)
err = cc3.Open(c3, nil)
require.NoError(t, err)
cc4 := AllocCodecContext(nil)
require.NotNil(t, cc4)
defer cc4.Free()
cc4.SetBitRate(1)
cc4.SetChannelLayout(ChannelLayout21)
cc4.SetChannels(3)
cc4.SetFlags(NewCodecContextFlags(4))
cc4.SetFlags2(NewCodecContextFlags2(5))
cc4.SetFramerate(NewRational(6, 1))
cc4.SetGopSize(7)
cc4.SetHeight(8)
cc4.SetLevel(16)
cc4.SetProfile(ProfileH264Extended)
cc4.SetPixelFormat(PixelFormat0Bgr)
cc4.SetQmin(5)
cc4.SetSampleAspectRatio(NewRational(10, 1))
cc4.SetSampleFormat(SampleFormatDbl)
cc4.SetSampleRate(12)
cc4.SetStrictStdCompliance(StrictStdComplianceExperimental)
cc4.SetThreadCount(13)
cc4.SetThreadType(ThreadTypeSlice)
cc4.SetTimeBase(NewRational(15, 1))
cc4.SetWidth(16)
cc4.SetExtraHardwareFrames(4)
require.Equal(t, int64(1), cc4.BitRate())
require.True(t, cc4.ChannelLayout().Equal(ChannelLayout21))
require.Equal(t, 3, cc4.Channels())
require.Equal(t, NewCodecContextFlags(4), cc4.Flags())
require.Equal(t, NewCodecContextFlags2(5), cc4.Flags2())
require.Equal(t, NewRational(6, 1), cc4.Framerate())
require.Equal(t, 7, cc4.GopSize())
require.Equal(t, 8, cc4.Height())
require.Equal(t, Level(16), cc4.Level())
require.Equal(t, ProfileH264Extended, cc4.Profile())
require.Equal(t, PixelFormat0Bgr, cc4.PixelFormat())
require.Equal(t, 5, cc4.Qmin())
require.Equal(t, NewRational(10, 1), cc4.SampleAspectRatio())
require.Equal(t, SampleFormatDbl, cc4.SampleFormat())
require.Equal(t, 12, cc4.SampleRate())
require.Equal(t, StrictStdComplianceExperimental, cc4.StrictStdCompliance())
require.Equal(t, 13, cc4.ThreadCount())
require.Equal(t, ThreadTypeSlice, cc4.ThreadType())
require.Equal(t, NewRational(15, 1), cc4.TimeBase())
require.Equal(t, 16, cc4.Width())
require.Equal(t, 4, cc4.ExtraHardwareFrames())
cc5 := AllocCodecContext(nil)
require.NotNil(t, cc5)
defer cc5.Free()
err = cc5.FromCodecParameters(s2.CodecParameters())
require.NoError(t, err)
require.Equal(t, 2, cc5.Channels())
cp1 := AllocCodecParameters()
require.NotNil(t, cp1)
defer cp1.Free()
err = cc5.ToCodecParameters(cp1)
require.NoError(t, err)
require.Equal(t, 2, cp1.Channels())
cc6 := AllocCodecContext(nil)
require.NotNil(t, cc6)
b := []byte("test")
require.NoError(t, cc6.SetExtraData(b))
require.Equal(t, b, cc6.ExtraData())
// TODO Test ReceivePacket
// TODO Test SendPacket
// TODO Test ReceiveFrame
// TODO Test SendFrame
}

View File

@@ -1,26 +0,0 @@
package astiav
//#cgo pkg-config: libavcodec
//#include <libavcodec/avcodec.h>
import "C"
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavcodec/codec.h#L460
type CodecHardwareConfig struct {
c *C.AVCodecHWConfig
}
func newCodecHardwareConfigFromC(c *C.AVCodecHWConfig) CodecHardwareConfig {
return CodecHardwareConfig{c: c}
}
func (chc CodecHardwareConfig) HardwareDeviceType() HardwareDeviceType {
return HardwareDeviceType(chc.c.device_type)
}
func (chc CodecHardwareConfig) MethodFlags() CodecHardwareConfigMethodFlags {
return CodecHardwareConfigMethodFlags(chc.c.methods)
}
func (chc CodecHardwareConfig) PixelFormat() PixelFormat {
return PixelFormat(chc.c.pix_fmt)
}

View File

@@ -1,15 +0,0 @@
package astiav
//#cgo pkg-config: libavcodec
//#include <libavcodec/avcodec.h>
import "C"
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavcodec/codec.h#L420
type CodecHardwareConfigMethodFlag int64
const (
CodecHardwareConfigMethodFlagAdHoc = CodecHardwareConfigMethodFlag(C.AV_CODEC_HW_CONFIG_METHOD_AD_HOC)
CodecHardwareConfigMethodFlagHwDeviceCtx = CodecHardwareConfigMethodFlag(C.AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX)
CodecHardwareConfigMethodFlagHwFramesCtx = CodecHardwareConfigMethodFlag(C.AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX)
CodecHardwareConfigMethodFlagInternal = CodecHardwareConfigMethodFlag(C.AV_CODEC_HW_CONFIG_METHOD_INTERNAL)
)

View File

@@ -1,430 +0,0 @@
package astiav
//#cgo pkg-config: libavcodec libavformat
//#include <libavcodec/avcodec.h>
//#include <libavformat/avformat.h>
import "C"
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavcodec/codec_id.h#L47
type CodecID C.enum_AVCodecID
const (
CodecID012V = CodecID(C.AV_CODEC_ID_012V)
CodecID4Xm = CodecID(C.AV_CODEC_ID_4XM)
CodecID8Bps = CodecID(C.AV_CODEC_ID_8BPS)
CodecID8SvxExp = CodecID(C.AV_CODEC_ID_8SVX_EXP)
CodecID8SvxFib = CodecID(C.AV_CODEC_ID_8SVX_FIB)
CodecIDA64Multi = CodecID(C.AV_CODEC_ID_A64_MULTI)
CodecIDA64Multi5 = CodecID(C.AV_CODEC_ID_A64_MULTI5)
CodecIDAac = CodecID(C.AV_CODEC_ID_AAC)
CodecIDAacLatm = CodecID(C.AV_CODEC_ID_AAC_LATM)
CodecIDAasc = CodecID(C.AV_CODEC_ID_AASC)
CodecIDAc3 = CodecID(C.AV_CODEC_ID_AC3)
CodecIDAdpcm4Xm = CodecID(C.AV_CODEC_ID_ADPCM_4XM)
CodecIDAdpcmAdx = CodecID(C.AV_CODEC_ID_ADPCM_ADX)
CodecIDAdpcmAfc = CodecID(C.AV_CODEC_ID_ADPCM_AFC)
CodecIDAdpcmCt = CodecID(C.AV_CODEC_ID_ADPCM_CT)
CodecIDAdpcmDtk = CodecID(C.AV_CODEC_ID_ADPCM_DTK)
CodecIDAdpcmEa = CodecID(C.AV_CODEC_ID_ADPCM_EA)
CodecIDAdpcmEaMaxisXa = CodecID(C.AV_CODEC_ID_ADPCM_EA_MAXIS_XA)
CodecIDAdpcmEaR1 = CodecID(C.AV_CODEC_ID_ADPCM_EA_R1)
CodecIDAdpcmEaR2 = CodecID(C.AV_CODEC_ID_ADPCM_EA_R2)
CodecIDAdpcmEaR3 = CodecID(C.AV_CODEC_ID_ADPCM_EA_R3)
CodecIDAdpcmEaXas = CodecID(C.AV_CODEC_ID_ADPCM_EA_XAS)
CodecIDAdpcmG722 = CodecID(C.AV_CODEC_ID_ADPCM_G722)
CodecIDAdpcmG726 = CodecID(C.AV_CODEC_ID_ADPCM_G726)
CodecIDAdpcmG726Le = CodecID(C.AV_CODEC_ID_ADPCM_G726LE)
CodecIDAdpcmImaAmv = CodecID(C.AV_CODEC_ID_ADPCM_IMA_AMV)
CodecIDAdpcmImaApc = CodecID(C.AV_CODEC_ID_ADPCM_IMA_APC)
CodecIDAdpcmImaDk3 = CodecID(C.AV_CODEC_ID_ADPCM_IMA_DK3)
CodecIDAdpcmImaDk4 = CodecID(C.AV_CODEC_ID_ADPCM_IMA_DK4)
CodecIDAdpcmImaEaEacs = CodecID(C.AV_CODEC_ID_ADPCM_IMA_EA_EACS)
CodecIDAdpcmImaEaSead = CodecID(C.AV_CODEC_ID_ADPCM_IMA_EA_SEAD)
CodecIDAdpcmImaIss = CodecID(C.AV_CODEC_ID_ADPCM_IMA_ISS)
CodecIDAdpcmImaOki = CodecID(C.AV_CODEC_ID_ADPCM_IMA_OKI)
CodecIDAdpcmImaQt = CodecID(C.AV_CODEC_ID_ADPCM_IMA_QT)
CodecIDAdpcmImaRad = CodecID(C.AV_CODEC_ID_ADPCM_IMA_RAD)
CodecIDAdpcmImaSmjpeg = CodecID(C.AV_CODEC_ID_ADPCM_IMA_SMJPEG)
CodecIDAdpcmImaWav = CodecID(C.AV_CODEC_ID_ADPCM_IMA_WAV)
CodecIDAdpcmImaWs = CodecID(C.AV_CODEC_ID_ADPCM_IMA_WS)
CodecIDAdpcmMs = CodecID(C.AV_CODEC_ID_ADPCM_MS)
CodecIDAdpcmSbpro2 = CodecID(C.AV_CODEC_ID_ADPCM_SBPRO_2)
CodecIDAdpcmSbpro3 = CodecID(C.AV_CODEC_ID_ADPCM_SBPRO_3)
CodecIDAdpcmSbpro4 = CodecID(C.AV_CODEC_ID_ADPCM_SBPRO_4)
CodecIDAdpcmSwf = CodecID(C.AV_CODEC_ID_ADPCM_SWF)
CodecIDAdpcmThp = CodecID(C.AV_CODEC_ID_ADPCM_THP)
CodecIDAdpcmVima = CodecID(C.AV_CODEC_ID_ADPCM_VIMA)
CodecIDAdpcmVimaDeprecated = CodecID(C.AV_CODEC_ID_ADPCM_VIMA)
CodecIDAdpcmXa = CodecID(C.AV_CODEC_ID_ADPCM_XA)
CodecIDAdpcmYamaha = CodecID(C.AV_CODEC_ID_ADPCM_YAMAHA)
CodecIDAic = CodecID(C.AV_CODEC_ID_AIC)
CodecIDAlac = CodecID(C.AV_CODEC_ID_ALAC)
CodecIDAliasPix = CodecID(C.AV_CODEC_ID_ALIAS_PIX)
CodecIDAmrNb = CodecID(C.AV_CODEC_ID_AMR_NB)
CodecIDAmrWb = CodecID(C.AV_CODEC_ID_AMR_WB)
CodecIDAmv = CodecID(C.AV_CODEC_ID_AMV)
CodecIDAnm = CodecID(C.AV_CODEC_ID_ANM)
CodecIDAnsi = CodecID(C.AV_CODEC_ID_ANSI)
CodecIDApe = CodecID(C.AV_CODEC_ID_APE)
CodecIDAss = CodecID(C.AV_CODEC_ID_ASS)
CodecIDAsv1 = CodecID(C.AV_CODEC_ID_ASV1)
CodecIDAsv2 = CodecID(C.AV_CODEC_ID_ASV2)
CodecIDAtrac1 = CodecID(C.AV_CODEC_ID_ATRAC1)
CodecIDAtrac3 = CodecID(C.AV_CODEC_ID_ATRAC3)
CodecIDAtrac3P = CodecID(C.AV_CODEC_ID_ATRAC3P)
CodecIDAura = CodecID(C.AV_CODEC_ID_AURA)
CodecIDAura2 = CodecID(C.AV_CODEC_ID_AURA2)
CodecIDAvrn = CodecID(C.AV_CODEC_ID_AVRN)
CodecIDAvrp = CodecID(C.AV_CODEC_ID_AVRP)
CodecIDAvs = CodecID(C.AV_CODEC_ID_AVS)
CodecIDAvui = CodecID(C.AV_CODEC_ID_AVUI)
CodecIDAyuv = CodecID(C.AV_CODEC_ID_AYUV)
CodecIDBethsoftvid = CodecID(C.AV_CODEC_ID_BETHSOFTVID)
CodecIDBfi = CodecID(C.AV_CODEC_ID_BFI)
CodecIDBinData = CodecID(C.AV_CODEC_ID_BIN_DATA)
CodecIDBinkaudioDct = CodecID(C.AV_CODEC_ID_BINKAUDIO_DCT)
CodecIDBinkaudioRdft = CodecID(C.AV_CODEC_ID_BINKAUDIO_RDFT)
CodecIDBinkvideo = CodecID(C.AV_CODEC_ID_BINKVIDEO)
CodecIDBCodecIDext = CodecID(C.AV_CODEC_ID_BINTEXT)
CodecIDBmp = CodecID(C.AV_CODEC_ID_BMP)
CodecIDBmvAudio = CodecID(C.AV_CODEC_ID_BMV_AUDIO)
CodecIDBmvVideo = CodecID(C.AV_CODEC_ID_BMV_VIDEO)
CodecIDBrenderPix = CodecID(C.AV_CODEC_ID_BRENDER_PIX)
CodecIDBrenderPixDeprecated = CodecID(C.AV_CODEC_ID_BRENDER_PIX)
CodecIDC93 = CodecID(C.AV_CODEC_ID_C93)
CodecIDCavs = CodecID(C.AV_CODEC_ID_CAVS)
CodecIDCdgraphics = CodecID(C.AV_CODEC_ID_CDGRAPHICS)
CodecIDCdxl = CodecID(C.AV_CODEC_ID_CDXL)
CodecIDCelt = CodecID(C.AV_CODEC_ID_CELT)
CodecIDCinepak = CodecID(C.AV_CODEC_ID_CINEPAK)
CodecIDCljr = CodecID(C.AV_CODEC_ID_CLJR)
CodecIDCllc = CodecID(C.AV_CODEC_ID_CLLC)
CodecIDCmv = CodecID(C.AV_CODEC_ID_CMV)
CodecIDComfortNoise = CodecID(C.AV_CODEC_ID_COMFORT_NOISE)
CodecIDCook = CodecID(C.AV_CODEC_ID_COOK)
CodecIDCpia = CodecID(C.AV_CODEC_ID_CPIA)
CodecIDCscd = CodecID(C.AV_CODEC_ID_CSCD)
CodecIDCyuv = CodecID(C.AV_CODEC_ID_CYUV)
CodecIDDfa = CodecID(C.AV_CODEC_ID_DFA)
CodecIDDirac = CodecID(C.AV_CODEC_ID_DIRAC)
CodecIDDnxhd = CodecID(C.AV_CODEC_ID_DNXHD)
CodecIDDpx = CodecID(C.AV_CODEC_ID_DPX)
CodecIDDsdLsbf = CodecID(C.AV_CODEC_ID_DSD_LSBF)
CodecIDDsdLsbfPlanar = CodecID(C.AV_CODEC_ID_DSD_LSBF_PLANAR)
CodecIDDsdMsbf = CodecID(C.AV_CODEC_ID_DSD_MSBF)
CodecIDDsdMsbfPlanar = CodecID(C.AV_CODEC_ID_DSD_MSBF_PLANAR)
CodecIDDsicinaudio = CodecID(C.AV_CODEC_ID_DSICINAUDIO)
CodecIDDsicinvideo = CodecID(C.AV_CODEC_ID_DSICINVIDEO)
CodecIDDts = CodecID(C.AV_CODEC_ID_DTS)
CodecIDDvaudio = CodecID(C.AV_CODEC_ID_DVAUDIO)
CodecIDDvbSubtitle = CodecID(C.AV_CODEC_ID_DVB_SUBTITLE)
CodecIDDvbTeletext = CodecID(C.AV_CODEC_ID_DVB_TELETEXT)
CodecIDDvdNav = CodecID(C.AV_CODEC_ID_DVD_NAV)
CodecIDDvdSubtitle = CodecID(C.AV_CODEC_ID_DVD_SUBTITLE)
CodecIDDvvideo = CodecID(C.AV_CODEC_ID_DVVIDEO)
CodecIDDxa = CodecID(C.AV_CODEC_ID_DXA)
CodecIDDxtory = CodecID(C.AV_CODEC_ID_DXTORY)
CodecIDEac3 = CodecID(C.AV_CODEC_ID_EAC3)
CodecIDEia608 = CodecID(C.AV_CODEC_ID_EIA_608)
CodecIDEscape124 = CodecID(C.AV_CODEC_ID_ESCAPE124)
CodecIDEscape130 = CodecID(C.AV_CODEC_ID_ESCAPE130)
CodecIDEscape130Deprecated = CodecID(C.AV_CODEC_ID_ESCAPE130)
CodecIDEvrc = CodecID(C.AV_CODEC_ID_EVRC)
CodecIDExr = CodecID(C.AV_CODEC_ID_EXR)
CodecIDExrDeprecated = CodecID(C.AV_CODEC_ID_EXR)
CodecIDFfmetadata = CodecID(C.AV_CODEC_ID_FFMETADATA)
CodecIDFfv1 = CodecID(C.AV_CODEC_ID_FFV1)
CodecIDFfvhuff = CodecID(C.AV_CODEC_ID_FFVHUFF)
CodecIDFfwavesynth = CodecID(C.AV_CODEC_ID_FFWAVESYNTH)
CodecIDFic = CodecID(C.AV_CODEC_ID_FIC)
CodecIDFirstAudio = CodecID(C.AV_CODEC_ID_FIRST_AUDIO)
CodecIDFirstSubtitle = CodecID(C.AV_CODEC_ID_FIRST_SUBTITLE)
CodecIDFirstUnknown = CodecID(C.AV_CODEC_ID_FIRST_UNKNOWN)
CodecIDFlac = CodecID(C.AV_CODEC_ID_FLAC)
CodecIDFlashsv = CodecID(C.AV_CODEC_ID_FLASHSV)
CodecIDFlashsv2 = CodecID(C.AV_CODEC_ID_FLASHSV2)
CodecIDFlic = CodecID(C.AV_CODEC_ID_FLIC)
CodecIDFlv1 = CodecID(C.AV_CODEC_ID_FLV1)
CodecIDFraps = CodecID(C.AV_CODEC_ID_FRAPS)
CodecIDFrwu = CodecID(C.AV_CODEC_ID_FRWU)
CodecIDG2M = CodecID(C.AV_CODEC_ID_G2M)
CodecIDG2MDeprecated = CodecID(C.AV_CODEC_ID_G2M)
CodecIDG7231 = CodecID(C.AV_CODEC_ID_G723_1)
CodecIDG729 = CodecID(C.AV_CODEC_ID_G729)
CodecIDGif = CodecID(C.AV_CODEC_ID_GIF)
CodecIDGsm = CodecID(C.AV_CODEC_ID_GSM)
CodecIDGsmMs = CodecID(C.AV_CODEC_ID_GSM_MS)
CodecIDH261 = CodecID(C.AV_CODEC_ID_H261)
CodecIDH263 = CodecID(C.AV_CODEC_ID_H263)
CodecIDH263I = CodecID(C.AV_CODEC_ID_H263I)
CodecIDH263P = CodecID(C.AV_CODEC_ID_H263P)
CodecIDH264 = CodecID(C.AV_CODEC_ID_H264)
CodecIDH265 = CodecID(C.AV_CODEC_ID_H265)
CodecIDHdmvPgsSubtitle = CodecID(C.AV_CODEC_ID_HDMV_PGS_SUBTITLE)
CodecIDHevc = CodecID(C.AV_CODEC_ID_HEVC)
CodecIDHevcDeprecated = CodecID(C.AV_CODEC_ID_HEVC)
CodecIDHnm4Video = CodecID(C.AV_CODEC_ID_HNM4_VIDEO)
CodecIDHuffyuv = CodecID(C.AV_CODEC_ID_HUFFYUV)
CodecIDIac = CodecID(C.AV_CODEC_ID_IAC)
CodecIDIdcin = CodecID(C.AV_CODEC_ID_IDCIN)
CodecIDIdf = CodecID(C.AV_CODEC_ID_IDF)
CodecIDIffByterun1 = CodecID(C.AV_CODEC_ID_IFF_BYTERUN1)
CodecIDIffIlbm = CodecID(C.AV_CODEC_ID_IFF_ILBM)
CodecIDIlbc = CodecID(C.AV_CODEC_ID_ILBC)
CodecIDImc = CodecID(C.AV_CODEC_ID_IMC)
CodecIDIndeo2 = CodecID(C.AV_CODEC_ID_INDEO2)
CodecIDIndeo3 = CodecID(C.AV_CODEC_ID_INDEO3)
CodecIDIndeo4 = CodecID(C.AV_CODEC_ID_INDEO4)
CodecIDIndeo5 = CodecID(C.AV_CODEC_ID_INDEO5)
CodecIDInterplayDpcm = CodecID(C.AV_CODEC_ID_INTERPLAY_DPCM)
CodecIDInterplayVideo = CodecID(C.AV_CODEC_ID_INTERPLAY_VIDEO)
CodecIDJacosub = CodecID(C.AV_CODEC_ID_JACOSUB)
CodecIDJpeg2000 = CodecID(C.AV_CODEC_ID_JPEG2000)
CodecIDJpegls = CodecID(C.AV_CODEC_ID_JPEGLS)
CodecIDJv = CodecID(C.AV_CODEC_ID_JV)
CodecIDKgv1 = CodecID(C.AV_CODEC_ID_KGV1)
CodecIDKmvc = CodecID(C.AV_CODEC_ID_KMVC)
CodecIDLagarith = CodecID(C.AV_CODEC_ID_LAGARITH)
CodecIDLjpeg = CodecID(C.AV_CODEC_ID_LJPEG)
CodecIDLoco = CodecID(C.AV_CODEC_ID_LOCO)
CodecIDMace3 = CodecID(C.AV_CODEC_ID_MACE3)
CodecIDMace6 = CodecID(C.AV_CODEC_ID_MACE6)
CodecIDMad = CodecID(C.AV_CODEC_ID_MAD)
CodecIDMdec = CodecID(C.AV_CODEC_ID_MDEC)
CodecIDMetasound = CodecID(C.AV_CODEC_ID_METASOUND)
CodecIDMicrodvd = CodecID(C.AV_CODEC_ID_MICRODVD)
CodecIDMimic = CodecID(C.AV_CODEC_ID_MIMIC)
CodecIDMjpeg = CodecID(C.AV_CODEC_ID_MJPEG)
CodecIDMjpegb = CodecID(C.AV_CODEC_ID_MJPEGB)
CodecIDMlp = CodecID(C.AV_CODEC_ID_MLP)
CodecIDMmvideo = CodecID(C.AV_CODEC_ID_MMVIDEO)
CodecIDMotionpixels = CodecID(C.AV_CODEC_ID_MOTIONPIXELS)
CodecIDMovText = CodecID(C.AV_CODEC_ID_MOV_TEXT)
CodecIDMp1 = CodecID(C.AV_CODEC_ID_MP1)
CodecIDMp2 = CodecID(C.AV_CODEC_ID_MP2)
CodecIDMp3 = CodecID(C.AV_CODEC_ID_MP3)
CodecIDMp3Adu = CodecID(C.AV_CODEC_ID_MP3ADU)
CodecIDMp3On4 = CodecID(C.AV_CODEC_ID_MP3ON4)
CodecIDMp4Als = CodecID(C.AV_CODEC_ID_MP4ALS)
CodecIDMpeg1Video = CodecID(C.AV_CODEC_ID_MPEG1VIDEO)
CodecIDMpeg2Ts = CodecID(C.AV_CODEC_ID_MPEG2TS)
CodecIDMpeg2Video = CodecID(C.AV_CODEC_ID_MPEG2VIDEO)
CodecIDMpeg4 = CodecID(C.AV_CODEC_ID_MPEG4)
CodecIDMpeg4Systems = CodecID(C.AV_CODEC_ID_MPEG4SYSTEMS)
CodecIDMpl2 = CodecID(C.AV_CODEC_ID_MPL2)
CodecIDMsa1 = CodecID(C.AV_CODEC_ID_MSA1)
CodecIDMsmpeg4V1 = CodecID(C.AV_CODEC_ID_MSMPEG4V1)
CodecIDMsmpeg4V2 = CodecID(C.AV_CODEC_ID_MSMPEG4V2)
CodecIDMsmpeg4V3 = CodecID(C.AV_CODEC_ID_MSMPEG4V3)
CodecIDMsrle = CodecID(C.AV_CODEC_ID_MSRLE)
CodecIDMss1 = CodecID(C.AV_CODEC_ID_MSS1)
CodecIDMss2 = CodecID(C.AV_CODEC_ID_MSS2)
CodecIDMsvideo1 = CodecID(C.AV_CODEC_ID_MSVIDEO1)
CodecIDMszh = CodecID(C.AV_CODEC_ID_MSZH)
CodecIDMts2 = CodecID(C.AV_CODEC_ID_MTS2)
CodecIDMusepack7 = CodecID(C.AV_CODEC_ID_MUSEPACK7)
CodecIDMusepack8 = CodecID(C.AV_CODEC_ID_MUSEPACK8)
CodecIDMvc1 = CodecID(C.AV_CODEC_ID_MVC1)
CodecIDMvc1Deprecated = CodecID(C.AV_CODEC_ID_MVC1)
CodecIDMvc2 = CodecID(C.AV_CODEC_ID_MVC2)
CodecIDMvc2Deprecated = CodecID(C.AV_CODEC_ID_MVC2)
CodecIDMxpeg = CodecID(C.AV_CODEC_ID_MXPEG)
CodecIDNellymoser = CodecID(C.AV_CODEC_ID_NELLYMOSER)
CodecIDNone = CodecID(C.AV_CODEC_ID_NONE)
CodecIDNuv = CodecID(C.AV_CODEC_ID_NUV)
CodecIDOn2Avc = CodecID(C.AV_CODEC_ID_ON2AVC)
CodecIDOpus = CodecID(C.AV_CODEC_ID_OPUS)
CodecIDOpusDeprecated = CodecID(C.AV_CODEC_ID_OPUS)
CodecIDOtf = CodecID(C.AV_CODEC_ID_OTF)
CodecIDPafAudio = CodecID(C.AV_CODEC_ID_PAF_AUDIO)
CodecIDPafAudioDeprecated = CodecID(C.AV_CODEC_ID_PAF_AUDIO)
CodecIDPafVideo = CodecID(C.AV_CODEC_ID_PAF_VIDEO)
CodecIDPafVideoDeprecated = CodecID(C.AV_CODEC_ID_PAF_VIDEO)
CodecIDPam = CodecID(C.AV_CODEC_ID_PAM)
CodecIDPbm = CodecID(C.AV_CODEC_ID_PBM)
CodecIDPcmAlaw = CodecID(C.AV_CODEC_ID_PCM_ALAW)
CodecIDPcmBluray = CodecID(C.AV_CODEC_ID_PCM_BLURAY)
CodecIDPcmDvd = CodecID(C.AV_CODEC_ID_PCM_DVD)
CodecIDPcmF32Be = CodecID(C.AV_CODEC_ID_PCM_F32BE)
CodecIDPcmF32Le = CodecID(C.AV_CODEC_ID_PCM_F32LE)
CodecIDPcmF64Be = CodecID(C.AV_CODEC_ID_PCM_F64BE)
CodecIDPcmF64Le = CodecID(C.AV_CODEC_ID_PCM_F64LE)
CodecIDPcmLxf = CodecID(C.AV_CODEC_ID_PCM_LXF)
CodecIDPcmMulaw = CodecID(C.AV_CODEC_ID_PCM_MULAW)
CodecIDPcmS16Be = CodecID(C.AV_CODEC_ID_PCM_S16BE)
CodecIDPcmS16BePlanar = CodecID(C.AV_CODEC_ID_PCM_S16BE_PLANAR)
CodecIDPcmS16Le = CodecID(C.AV_CODEC_ID_PCM_S16LE)
CodecIDPcmS16LePlanar = CodecID(C.AV_CODEC_ID_PCM_S16LE_PLANAR)
CodecIDPcmS24Be = CodecID(C.AV_CODEC_ID_PCM_S24BE)
CodecIDPcmS24Daud = CodecID(C.AV_CODEC_ID_PCM_S24DAUD)
CodecIDPcmS24Le = CodecID(C.AV_CODEC_ID_PCM_S24LE)
CodecIDPcmS24LePlanar = CodecID(C.AV_CODEC_ID_PCM_S24LE_PLANAR)
CodecIDPcmS24LePlanarDeprecated = CodecID(C.AV_CODEC_ID_PCM_S24LE_PLANAR)
CodecIDPcmS32Be = CodecID(C.AV_CODEC_ID_PCM_S32BE)
CodecIDPcmS32Le = CodecID(C.AV_CODEC_ID_PCM_S32LE)
CodecIDPcmS32LePlanar = CodecID(C.AV_CODEC_ID_PCM_S32LE_PLANAR)
CodecIDPcmS32LePlanarDeprecated = CodecID(C.AV_CODEC_ID_PCM_S32LE_PLANAR)
CodecIDPcmS8 = CodecID(C.AV_CODEC_ID_PCM_S8)
CodecIDPcmS8Planar = CodecID(C.AV_CODEC_ID_PCM_S8_PLANAR)
CodecIDPcmU16Be = CodecID(C.AV_CODEC_ID_PCM_U16BE)
CodecIDPcmU16Le = CodecID(C.AV_CODEC_ID_PCM_U16LE)
CodecIDPcmU24Be = CodecID(C.AV_CODEC_ID_PCM_U24BE)
CodecIDPcmU24Le = CodecID(C.AV_CODEC_ID_PCM_U24LE)
CodecIDPcmU32Be = CodecID(C.AV_CODEC_ID_PCM_U32BE)
CodecIDPcmU32Le = CodecID(C.AV_CODEC_ID_PCM_U32LE)
CodecIDPcmU8 = CodecID(C.AV_CODEC_ID_PCM_U8)
CodecIDPcmZork = CodecID(C.AV_CODEC_ID_PCM_ZORK)
CodecIDPcx = CodecID(C.AV_CODEC_ID_PCX)
CodecIDPgm = CodecID(C.AV_CODEC_ID_PGM)
CodecIDPgmyuv = CodecID(C.AV_CODEC_ID_PGMYUV)
CodecIDPictor = CodecID(C.AV_CODEC_ID_PICTOR)
CodecIDPjs = CodecID(C.AV_CODEC_ID_PJS)
CodecIDPng = CodecID(C.AV_CODEC_ID_PNG)
CodecIDPpm = CodecID(C.AV_CODEC_ID_PPM)
CodecIDProbe = CodecID(C.AV_CODEC_ID_PROBE)
CodecIDProres = CodecID(C.AV_CODEC_ID_PRORES)
CodecIDPtx = CodecID(C.AV_CODEC_ID_PTX)
CodecIDQcelp = CodecID(C.AV_CODEC_ID_QCELP)
CodecIDQdm2 = CodecID(C.AV_CODEC_ID_QDM2)
CodecIDQdmc = CodecID(C.AV_CODEC_ID_QDMC)
CodecIDQdraw = CodecID(C.AV_CODEC_ID_QDRAW)
CodecIDQpeg = CodecID(C.AV_CODEC_ID_QPEG)
CodecIDQtrle = CodecID(C.AV_CODEC_ID_QTRLE)
CodecIDR10K = CodecID(C.AV_CODEC_ID_R10K)
CodecIDR210 = CodecID(C.AV_CODEC_ID_R210)
CodecIDRa144 = CodecID(C.AV_CODEC_ID_RA_144)
CodecIDRa288 = CodecID(C.AV_CODEC_ID_RA_288)
CodecIDRalf = CodecID(C.AV_CODEC_ID_RALF)
CodecIDRawvideo = CodecID(C.AV_CODEC_ID_RAWVIDEO)
CodecIDRealtext = CodecID(C.AV_CODEC_ID_REALTEXT)
CodecIDRl2 = CodecID(C.AV_CODEC_ID_RL2)
CodecIDRoq = CodecID(C.AV_CODEC_ID_ROQ)
CodecIDRoqDpcm = CodecID(C.AV_CODEC_ID_ROQ_DPCM)
CodecIDRpza = CodecID(C.AV_CODEC_ID_RPZA)
CodecIDRv10 = CodecID(C.AV_CODEC_ID_RV10)
CodecIDRv20 = CodecID(C.AV_CODEC_ID_RV20)
CodecIDRv30 = CodecID(C.AV_CODEC_ID_RV30)
CodecIDRv40 = CodecID(C.AV_CODEC_ID_RV40)
CodecIDS302M = CodecID(C.AV_CODEC_ID_S302M)
CodecIDSami = CodecID(C.AV_CODEC_ID_SAMI)
CodecIDSanm = CodecID(C.AV_CODEC_ID_SANM)
CodecIDSanmDeprecated = CodecID(C.AV_CODEC_ID_SANM)
CodecIDSgi = CodecID(C.AV_CODEC_ID_SGI)
CodecIDSgirle = CodecID(C.AV_CODEC_ID_SGIRLE)
CodecIDSgirleDeprecated = CodecID(C.AV_CODEC_ID_SGIRLE)
CodecIDShorten = CodecID(C.AV_CODEC_ID_SHORTEN)
CodecIDSipr = CodecID(C.AV_CODEC_ID_SIPR)
CodecIDSmackaudio = CodecID(C.AV_CODEC_ID_SMACKAUDIO)
CodecIDSmackvideo = CodecID(C.AV_CODEC_ID_SMACKVIDEO)
CodecIDSmc = CodecID(C.AV_CODEC_ID_SMC)
CodecIDSmpteKlv = CodecID(C.AV_CODEC_ID_SMPTE_KLV)
CodecIDSmv = CodecID(C.AV_CODEC_ID_SMV)
CodecIDSmvjpeg = CodecID(C.AV_CODEC_ID_SMVJPEG)
CodecIDSnow = CodecID(C.AV_CODEC_ID_SNOW)
CodecIDSolDpcm = CodecID(C.AV_CODEC_ID_SOL_DPCM)
CodecIDSonic = CodecID(C.AV_CODEC_ID_SONIC)
CodecIDSonicLs = CodecID(C.AV_CODEC_ID_SONIC_LS)
CodecIDSp5X = CodecID(C.AV_CODEC_ID_SP5X)
CodecIDSpeex = CodecID(C.AV_CODEC_ID_SPEEX)
CodecIDSrt = CodecID(C.AV_CODEC_ID_SRT)
CodecIDSsa = CodecID(C.AV_CODEC_ID_SSA)
CodecIDSubrip = CodecID(C.AV_CODEC_ID_SUBRIP)
CodecIDSubviewer = CodecID(C.AV_CODEC_ID_SUBVIEWER)
CodecIDSubviewer1 = CodecID(C.AV_CODEC_ID_SUBVIEWER1)
CodecIDSunrast = CodecID(C.AV_CODEC_ID_SUNRAST)
CodecIDSvq1 = CodecID(C.AV_CODEC_ID_SVQ1)
CodecIDSvq3 = CodecID(C.AV_CODEC_ID_SVQ3)
CodecIDTak = CodecID(C.AV_CODEC_ID_TAK)
CodecIDTakDeprecated = CodecID(C.AV_CODEC_ID_TAK)
CodecIDTarga = CodecID(C.AV_CODEC_ID_TARGA)
CodecIDTargaY216 = CodecID(C.AV_CODEC_ID_TARGA_Y216)
CodecIDText = CodecID(C.AV_CODEC_ID_TEXT)
CodecIDTgq = CodecID(C.AV_CODEC_ID_TGQ)
CodecIDTgv = CodecID(C.AV_CODEC_ID_TGV)
CodecIDTheora = CodecID(C.AV_CODEC_ID_THEORA)
CodecIDThp = CodecID(C.AV_CODEC_ID_THP)
CodecIDTiertexseqvideo = CodecID(C.AV_CODEC_ID_TIERTEXSEQVIDEO)
CodecIDTiff = CodecID(C.AV_CODEC_ID_TIFF)
CodecIDTimedId3 = CodecID(C.AV_CODEC_ID_TIMED_ID3)
CodecIDTmv = CodecID(C.AV_CODEC_ID_TMV)
CodecIDTqi = CodecID(C.AV_CODEC_ID_TQI)
CodecIDTruehd = CodecID(C.AV_CODEC_ID_TRUEHD)
CodecIDTruemotion1 = CodecID(C.AV_CODEC_ID_TRUEMOTION1)
CodecIDTruemotion2 = CodecID(C.AV_CODEC_ID_TRUEMOTION2)
CodecIDTruespeech = CodecID(C.AV_CODEC_ID_TRUESPEECH)
CodecIDTscc = CodecID(C.AV_CODEC_ID_TSCC)
CodecIDTscc2 = CodecID(C.AV_CODEC_ID_TSCC2)
CodecIDTta = CodecID(C.AV_CODEC_ID_TTA)
CodecIDTtf = CodecID(C.AV_CODEC_ID_TTF)
CodecIDTwinvq = CodecID(C.AV_CODEC_ID_TWINVQ)
CodecIDTxd = CodecID(C.AV_CODEC_ID_TXD)
CodecIDUlti = CodecID(C.AV_CODEC_ID_ULTI)
CodecIDUtvideo = CodecID(C.AV_CODEC_ID_UTVIDEO)
CodecIDV210 = CodecID(C.AV_CODEC_ID_V210)
CodecIDV210X = CodecID(C.AV_CODEC_ID_V210X)
CodecIDV308 = CodecID(C.AV_CODEC_ID_V308)
CodecIDV408 = CodecID(C.AV_CODEC_ID_V408)
CodecIDV410 = CodecID(C.AV_CODEC_ID_V410)
CodecIDVb = CodecID(C.AV_CODEC_ID_VB)
CodecIDVble = CodecID(C.AV_CODEC_ID_VBLE)
CodecIDVc1 = CodecID(C.AV_CODEC_ID_VC1)
CodecIDVc1Image = CodecID(C.AV_CODEC_ID_VC1IMAGE)
CodecIDVcr1 = CodecID(C.AV_CODEC_ID_VCR1)
CodecIDVixl = CodecID(C.AV_CODEC_ID_VIXL)
CodecIDVmdaudio = CodecID(C.AV_CODEC_ID_VMDAUDIO)
CodecIDVmdvideo = CodecID(C.AV_CODEC_ID_VMDVIDEO)
CodecIDVmnc = CodecID(C.AV_CODEC_ID_VMNC)
CodecIDVorbis = CodecID(C.AV_CODEC_ID_VORBIS)
CodecIDVp3 = CodecID(C.AV_CODEC_ID_VP3)
CodecIDVp5 = CodecID(C.AV_CODEC_ID_VP5)
CodecIDVp6 = CodecID(C.AV_CODEC_ID_VP6)
CodecIDVp6A = CodecID(C.AV_CODEC_ID_VP6A)
CodecIDVp6F = CodecID(C.AV_CODEC_ID_VP6F)
CodecIDVp7 = CodecID(C.AV_CODEC_ID_VP7)
CodecIDVp7Deprecated = CodecID(C.AV_CODEC_ID_VP7)
CodecIDVp8 = CodecID(C.AV_CODEC_ID_VP8)
CodecIDVp9 = CodecID(C.AV_CODEC_ID_VP9)
CodecIDVplayer = CodecID(C.AV_CODEC_ID_VPLAYER)
CodecIDWavpack = CodecID(C.AV_CODEC_ID_WAVPACK)
CodecIDWebp = CodecID(C.AV_CODEC_ID_WEBP)
CodecIDWebpDeprecated = CodecID(C.AV_CODEC_ID_WEBP)
CodecIDWebvtt = CodecID(C.AV_CODEC_ID_WEBVTT)
CodecIDWestwoodSnd1 = CodecID(C.AV_CODEC_ID_WESTWOOD_SND1)
CodecIDWmalossless = CodecID(C.AV_CODEC_ID_WMALOSSLESS)
CodecIDWmapro = CodecID(C.AV_CODEC_ID_WMAPRO)
CodecIDWmav1 = CodecID(C.AV_CODEC_ID_WMAV1)
CodecIDWmav2 = CodecID(C.AV_CODEC_ID_WMAV2)
CodecIDWmavoice = CodecID(C.AV_CODEC_ID_WMAVOICE)
CodecIDWmv1 = CodecID(C.AV_CODEC_ID_WMV1)
CodecIDWmv2 = CodecID(C.AV_CODEC_ID_WMV2)
CodecIDWmv3 = CodecID(C.AV_CODEC_ID_WMV3)
CodecIDWmv3Image = CodecID(C.AV_CODEC_ID_WMV3IMAGE)
CodecIDWnv1 = CodecID(C.AV_CODEC_ID_WNV1)
CodecIDWsVqa = CodecID(C.AV_CODEC_ID_WS_VQA)
CodecIDXanDpcm = CodecID(C.AV_CODEC_ID_XAN_DPCM)
CodecIDXanWc3 = CodecID(C.AV_CODEC_ID_XAN_WC3)
CodecIDXanWc4 = CodecID(C.AV_CODEC_ID_XAN_WC4)
CodecIDXbin = CodecID(C.AV_CODEC_ID_XBIN)
CodecIDXbm = CodecID(C.AV_CODEC_ID_XBM)
CodecIDXface = CodecID(C.AV_CODEC_ID_XFACE)
CodecIDXsub = CodecID(C.AV_CODEC_ID_XSUB)
CodecIDXwd = CodecID(C.AV_CODEC_ID_XWD)
CodecIDY41P = CodecID(C.AV_CODEC_ID_Y41P)
CodecIDYop = CodecID(C.AV_CODEC_ID_YOP)
CodecIDYuv4 = CodecID(C.AV_CODEC_ID_YUV4)
CodecIDZerocodec = CodecID(C.AV_CODEC_ID_ZEROCODEC)
CodecIDZlib = CodecID(C.AV_CODEC_ID_ZLIB)
CodecIDZmbv = CodecID(C.AV_CODEC_ID_ZMBV)
)
func (c CodecID) MediaType() MediaType {
return MediaType(C.avcodec_get_type((C.enum_AVCodecID)(c)))
}
func (c CodecID) Name() string {
return C.GoString(C.avcodec_get_name((C.enum_AVCodecID)(c)))
}
func (c CodecID) String() string {
return c.Name()
}

View File

@@ -1,13 +0,0 @@
package astiav
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestCodecID(t *testing.T) {
require.Equal(t, MediaTypeVideo, CodecIDH264.MediaType())
require.Equal(t, "h264", CodecIDH264.Name())
require.Equal(t, "h264", CodecIDH264.String())
}

View File

@@ -1,197 +0,0 @@
package astiav
//#cgo pkg-config: libavcodec
//#include <libavcodec/avcodec.h>
import "C"
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavcodec/codec_par.h#L52
type CodecParameters struct {
c *C.struct_AVCodecParameters
}
func AllocCodecParameters() *CodecParameters {
return newCodecParametersFromC(C.avcodec_parameters_alloc())
}
func newCodecParametersFromC(c *C.struct_AVCodecParameters) *CodecParameters {
if c == nil {
return nil
}
return &CodecParameters{c: c}
}
func (cp *CodecParameters) Free() {
C.avcodec_parameters_free(&cp.c)
}
func (cp *CodecParameters) BitRate() int64 {
return int64(cp.c.bit_rate)
}
func (cp *CodecParameters) ChannelLayout() ChannelLayout {
l, _ := newChannelLayoutFromC(&cp.c.ch_layout).clone()
return l
}
func (cp *CodecParameters) SetChannelLayout(l ChannelLayout) {
l.copy(&cp.c.ch_layout) //nolint: errcheck
}
func (cp *CodecParameters) Channels() int {
return int(cp.c.channels)
}
func (cp *CodecParameters) SetChannels(c int) {
cp.c.channels = C.int(c)
}
func (cp *CodecParameters) CodecID() CodecID {
return CodecID(cp.c.codec_id)
}
func (cp *CodecParameters) SetCodecID(i CodecID) {
cp.c.codec_id = uint32(i)
}
func (cp *CodecParameters) CodecTag() CodecTag {
return CodecTag(cp.c.codec_tag)
}
func (cp *CodecParameters) SetCodecTag(t CodecTag) {
cp.c.codec_tag = C.uint(t)
}
func (cp *CodecParameters) CodecType() MediaType {
return MediaType(cp.c.codec_type)
}
func (cp *CodecParameters) SetCodecType(t MediaType) {
cp.c.codec_type = int32(t)
}
func (cp *CodecParameters) ChromaLocation() ChromaLocation {
return ChromaLocation(cp.c.chroma_location)
}
func (cp *CodecParameters) ColorPrimaries() ColorPrimaries {
return ColorPrimaries(cp.c.color_primaries)
}
func (cp *CodecParameters) ColorRange() ColorRange {
return ColorRange(cp.c.color_range)
}
func (cp *CodecParameters) SetColorRange(r ColorRange) {
cp.c.color_range = C.enum_AVColorRange(r)
}
func (cp *CodecParameters) ColorSpace() ColorSpace {
return ColorSpace(cp.c.color_space)
}
func (cp *CodecParameters) ColorTransferCharacteristic() ColorTransferCharacteristic {
return ColorTransferCharacteristic(cp.c.color_trc)
}
func (cp *CodecParameters) ExtraData() []byte {
return bytesFromC(func(size *C.size_t) *C.uint8_t {
*size = C.size_t(cp.c.extradata_size)
return cp.c.extradata
})
}
func (cp *CodecParameters) SetExtraData(b []byte) error {
return setBytesWithIntSizeInC(b, &cp.c.extradata, &cp.c.extradata_size)
}
func (cp *CodecParameters) FrameSize() int {
return int(cp.c.frame_size)
}
func (cp *CodecParameters) SetFrameSize(i int) {
cp.c.frame_size = C.int(i)
}
func (cp *CodecParameters) Height() int {
return int(cp.c.height)
}
func (cp *CodecParameters) SetHeight(h int) {
cp.c.height = C.int(h)
}
func (cp *CodecParameters) Level() Level {
return Level(cp.c.level)
}
func (cp *CodecParameters) SetLevel(l Level) {
cp.c.level = C.int(l)
}
func (cp *CodecParameters) MediaType() MediaType {
return MediaType(cp.c.codec_type)
}
func (cp *CodecParameters) SetMediaType(t MediaType) {
cp.c.codec_type = C.enum_AVMediaType(t)
}
func (cp *CodecParameters) PixelFormat() PixelFormat {
return PixelFormat(cp.c.format)
}
func (cp *CodecParameters) SetPixelFormat(f PixelFormat) {
cp.c.format = C.int(f)
}
func (cp *CodecParameters) Profile() Profile {
return Profile(cp.c.profile)
}
func (cp *CodecParameters) SetProfile(p Profile) {
cp.c.profile = C.int(p)
}
func (cp *CodecParameters) SampleAspectRatio() Rational {
return newRationalFromC(cp.c.sample_aspect_ratio)
}
func (cp *CodecParameters) SetSampleAspectRatio(r Rational) {
cp.c.sample_aspect_ratio = r.c
}
func (cp *CodecParameters) SampleFormat() SampleFormat {
return SampleFormat(cp.c.format)
}
func (cp *CodecParameters) SetSampleFormat(f SampleFormat) {
cp.c.format = C.int(f)
}
func (cp *CodecParameters) SampleRate() int {
return int(cp.c.sample_rate)
}
func (cp *CodecParameters) SetSampleRate(r int) {
cp.c.sample_rate = C.int(r)
}
func (cp *CodecParameters) Width() int {
return int(cp.c.width)
}
func (cp *CodecParameters) SetWidth(w int) {
cp.c.width = C.int(w)
}
func (cp *CodecParameters) FromCodecContext(cc *CodecContext) error {
return newError(C.avcodec_parameters_from_context(cp.c, cc.c))
}
func (cp *CodecParameters) ToCodecContext(cc *CodecContext) error {
return newError(C.avcodec_parameters_to_context(cc.c, cp.c))
}
func (cp *CodecParameters) Copy(dst *CodecParameters) error {
return newError(C.avcodec_parameters_copy(dst.c, cp.c))
}

View File

@@ -1,105 +0,0 @@
package astiav
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestCodecParameters(t *testing.T) {
fc, err := globalHelper.inputFormatContext("video.mp4")
require.NoError(t, err)
ss := fc.Streams()
require.Len(t, ss, 2)
s1 := ss[0]
s2 := ss[1]
cp1 := s1.CodecParameters()
require.Equal(t, int64(441324), cp1.BitRate())
require.Equal(t, ChromaLocationLeft, cp1.ChromaLocation())
require.Equal(t, CodecIDH264, cp1.CodecID())
require.Equal(t, CodecTag(0x31637661), cp1.CodecTag())
require.Equal(t, ColorPrimariesUnspecified, cp1.ColorPrimaries())
require.Equal(t, ColorRangeUnspecified, cp1.ColorRange())
require.Equal(t, ColorSpaceUnspecified, cp1.ColorSpace())
require.Equal(t, ColorTransferCharacteristicUnspecified, cp1.ColorTransferCharacteristic())
require.Equal(t, 180, cp1.Height())
require.Equal(t, Level(13), cp1.Level())
require.Equal(t, MediaTypeVideo, cp1.MediaType())
require.Equal(t, PixelFormatYuv420P, cp1.PixelFormat())
require.Equal(t, ProfileH264ConstrainedBaseline, cp1.Profile())
require.Equal(t, NewRational(1, 1), cp1.SampleAspectRatio())
require.Equal(t, 320, cp1.Width())
cp2 := s2.CodecParameters()
require.Equal(t, int64(161052), cp2.BitRate())
require.Equal(t, 2, cp2.Channels())
require.True(t, cp2.ChannelLayout().Equal(ChannelLayoutStereo))
require.Equal(t, CodecIDAac, cp2.CodecID())
require.Equal(t, CodecTag(0x6134706d), cp2.CodecTag())
require.Equal(t, 1024, cp2.FrameSize())
require.Equal(t, MediaTypeAudio, cp2.MediaType())
require.Equal(t, SampleFormatFltp, cp2.SampleFormat())
require.Equal(t, 48000, cp2.SampleRate())
cp3 := AllocCodecParameters()
require.NotNil(t, cp3)
defer cp3.Free()
err = cp2.Copy(cp3)
require.NoError(t, err)
require.Equal(t, 2, cp3.Channels())
cc4 := AllocCodecContext(nil)
require.NotNil(t, cc4)
defer cc4.Free()
err = cp2.ToCodecContext(cc4)
require.NoError(t, err)
require.Equal(t, 2, cc4.Channels())
cp5 := AllocCodecParameters()
require.NotNil(t, cp5)
defer cp5.Free()
err = cp5.FromCodecContext(cc4)
require.NoError(t, err)
require.Equal(t, 2, cp5.Channels())
cp6 := AllocCodecParameters()
require.NotNil(t, cp6)
defer cp6.Free()
cp6.SetChannelLayout(ChannelLayout21)
require.True(t, cp6.ChannelLayout().Equal(ChannelLayout21))
defer cp6.Free()
cp6.SetChannels(3)
require.Equal(t, 3, cp6.Channels())
cp6.SetCodecID(CodecIDRawvideo)
require.Equal(t, CodecIDRawvideo, cp6.CodecID())
cp6.SetCodecTag(CodecTag(2))
require.Equal(t, CodecTag(2), cp6.CodecTag())
cp6.SetColorRange(ColorRangeJpeg)
require.Equal(t, ColorRangeJpeg, cp6.ColorRange())
cp6.SetCodecType(MediaTypeAudio)
require.Equal(t, MediaTypeAudio, cp6.CodecType())
cp6.SetFrameSize(1)
require.Equal(t, 1, cp6.FrameSize())
cp6.SetHeight(1)
require.Equal(t, 1, cp6.Height())
cp1.SetLevel(16)
require.Equal(t, Level(16), cp1.Level())
cp6.SetMediaType(MediaTypeAudio)
require.Equal(t, MediaTypeAudio, cp6.MediaType())
cp1.SetProfile(ProfileH264Extended)
require.Equal(t, ProfileH264Extended, cp1.Profile())
cp6.SetPixelFormat(PixelFormat0Bgr)
require.Equal(t, PixelFormat0Bgr, cp6.PixelFormat())
cp6.SetSampleAspectRatio(NewRational(1, 2))
require.Equal(t, NewRational(1, 2), cp6.SampleAspectRatio())
cp6.SetSampleFormat(SampleFormatDbl)
require.Equal(t, SampleFormatDbl, cp6.SampleFormat())
cp6.SetSampleRate(4)
require.Equal(t, 4, cp6.SampleRate())
cp6.SetWidth(2)
require.Equal(t, 2, cp6.Width())
b := []byte("test")
require.NoError(t, cp6.SetExtraData(b))
require.Equal(t, b, cp6.ExtraData())
}

View File

@@ -1,3 +0,0 @@
package astiav
type CodecTag uint32

View File

@@ -1,77 +0,0 @@
package astiav
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestCodec(t *testing.T) {
c := FindDecoder(CodecIDMp3)
require.NotNil(t, c)
require.Equal(t, c.ID(), CodecIDMp3)
require.Nil(t, c.ChannelLayouts())
require.True(t, c.IsDecoder())
require.False(t, c.IsEncoder())
require.Nil(t, c.PixelFormats())
require.Equal(t, []SampleFormat{SampleFormatFltp, SampleFormatFlt}, c.SampleFormats())
require.Equal(t, "mp3float", c.Name())
require.Equal(t, "mp3float", c.String())
c = FindDecoderByName("aac")
require.NotNil(t, c)
els := []ChannelLayout{
ChannelLayoutMono,
ChannelLayoutStereo,
ChannelLayoutSurround,
ChannelLayout4Point0,
ChannelLayout5Point0Back,
ChannelLayout5Point1Back,
ChannelLayout7Point1WideBack,
}
gls := c.ChannelLayouts()
require.Len(t, gls, len(els))
for idx := range els {
require.True(t, els[idx].Equal(gls[idx]))
}
require.True(t, c.IsDecoder())
require.False(t, c.IsEncoder())
require.Equal(t, []SampleFormat{SampleFormatFltp}, c.SampleFormats())
require.Equal(t, "aac", c.Name())
require.Equal(t, "aac", c.String())
c = FindEncoder(CodecIDMjpeg)
require.NotNil(t, c)
require.False(t, c.IsDecoder())
require.True(t, c.IsEncoder())
require.Contains(t, c.PixelFormats(), PixelFormatYuvj420P)
require.Nil(t, c.SampleFormats())
require.Contains(t, c.Name(), "mjpeg")
require.Contains(t, c.String(), "mjpeg")
c = FindEncoderByName("mjpeg")
require.NotNil(t, c)
require.False(t, c.IsDecoder())
require.True(t, c.IsEncoder())
require.Equal(t, []PixelFormat{
PixelFormatYuvj420P,
PixelFormatYuvj422P,
PixelFormatYuvj444P,
PixelFormatYuv420P,
PixelFormatYuv422P,
PixelFormatYuv444P,
}, c.PixelFormats())
require.Equal(t, "mjpeg", c.Name())
require.Equal(t, "mjpeg", c.String())
c = FindDecoderByName("invalid")
require.Nil(t, c)
var found bool
for _, c := range Codecs() {
if c.ID() == CodecIDMjpeg {
found = true
}
}
require.True(t, found)
}

View File

@@ -1,27 +0,0 @@
package astiav
//#cgo pkg-config: libavutil
//#include <libavutil/pixfmt.h>
import "C"
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavutil/pixfmt.h#L469
type ColorPrimaries C.enum_AVColorPrimaries
const (
ColorPrimariesReserved0 = ColorPrimaries(C.AVCOL_PRI_RESERVED0)
ColorPrimariesBt709 = ColorPrimaries(C.AVCOL_PRI_BT709)
ColorPrimariesUnspecified = ColorPrimaries(C.AVCOL_PRI_UNSPECIFIED)
ColorPrimariesReserved = ColorPrimaries(C.AVCOL_PRI_RESERVED)
ColorPrimariesBt470M = ColorPrimaries(C.AVCOL_PRI_BT470M)
ColorPrimariesBt470Bg = ColorPrimaries(C.AVCOL_PRI_BT470BG)
ColorPrimariesSmpte170M = ColorPrimaries(C.AVCOL_PRI_SMPTE170M)
ColorPrimariesSmpte240M = ColorPrimaries(C.AVCOL_PRI_SMPTE240M)
ColorPrimariesFilm = ColorPrimaries(C.AVCOL_PRI_FILM)
ColorPrimariesBt2020 = ColorPrimaries(C.AVCOL_PRI_BT2020)
ColorPrimariesSmpte428 = ColorPrimaries(C.AVCOL_PRI_SMPTE428)
ColorPrimariesSmptest4281 = ColorPrimaries(C.AVCOL_PRI_SMPTEST428_1)
ColorPrimariesSmpte431 = ColorPrimaries(C.AVCOL_PRI_SMPTE431)
ColorPrimariesSmpte432 = ColorPrimaries(C.AVCOL_PRI_SMPTE432)
ColorPrimariesJedecP22 = ColorPrimaries(C.AVCOL_PRI_JEDEC_P22)
ColorPrimariesNb = ColorPrimaries(C.AVCOL_PRI_NB)
)

View File

@@ -1,15 +0,0 @@
package astiav
//#cgo pkg-config: libavutil
//#include <libavutil/pixfmt.h>
import "C"
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavutil/pixfmt.h#L562
type ColorRange C.enum_AVColorRange
const (
ColorRangeUnspecified = ColorRange(C.AVCOL_RANGE_UNSPECIFIED)
ColorRangeMpeg = ColorRange(C.AVCOL_RANGE_MPEG)
ColorRangeJpeg = ColorRange(C.AVCOL_RANGE_JPEG)
ColorRangeNb = ColorRange(C.AVCOL_RANGE_NB)
)

View File

@@ -1,28 +0,0 @@
package astiav
//#cgo pkg-config: libavutil
//#include <libavutil/pixfmt.h>
import "C"
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavutil/pixfmt.h#L523
type ColorSpace C.enum_AVColorSpace
const (
ColorSpaceRgb = ColorSpace(C.AVCOL_SPC_RGB)
ColorSpaceBt709 = ColorSpace(C.AVCOL_SPC_BT709)
ColorSpaceUnspecified = ColorSpace(C.AVCOL_SPC_UNSPECIFIED)
ColorSpaceReserved = ColorSpace(C.AVCOL_SPC_RESERVED)
ColorSpaceFcc = ColorSpace(C.AVCOL_SPC_FCC)
ColorSpaceBt470Bg = ColorSpace(C.AVCOL_SPC_BT470BG)
ColorSpaceSmpte170M = ColorSpace(C.AVCOL_SPC_SMPTE170M)
ColorSpaceSmpte240M = ColorSpace(C.AVCOL_SPC_SMPTE240M)
ColorSpaceYcgco = ColorSpace(C.AVCOL_SPC_YCGCO)
ColorSpaceYcocg = ColorSpace(C.AVCOL_SPC_YCOCG)
ColorSpaceBt2020Ncl = ColorSpace(C.AVCOL_SPC_BT2020_NCL)
ColorSpaceBt2020Cl = ColorSpace(C.AVCOL_SPC_BT2020_CL)
ColorSpaceSmpte2085 = ColorSpace(C.AVCOL_SPC_SMPTE2085)
ColorSpaceChromaDerivedNcl = ColorSpace(C.AVCOL_SPC_CHROMA_DERIVED_NCL)
ColorSpaceChromaDerivedCl = ColorSpace(C.AVCOL_SPC_CHROMA_DERIVED_CL)
ColorSpaceIctcp = ColorSpace(C.AVCOL_SPC_ICTCP)
ColorSpaceNb = ColorSpace(C.AVCOL_SPC_NB)
)

View File

@@ -1,33 +0,0 @@
package astiav
//#cgo pkg-config: libavutil
//#include <libavutil/pixfmt.h>
import "C"
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavutil/pixfmt.h#L494
type ColorTransferCharacteristic C.enum_AVColorTransferCharacteristic
const (
ColorTransferCharacteristicReserved0 = ColorTransferCharacteristic(C.AVCOL_TRC_RESERVED0)
ColorTransferCharacteristicBt709 = ColorTransferCharacteristic(C.AVCOL_TRC_BT709)
ColorTransferCharacteristicUnspecified = ColorTransferCharacteristic(C.AVCOL_TRC_UNSPECIFIED)
ColorTransferCharacteristicReserved = ColorTransferCharacteristic(C.AVCOL_TRC_RESERVED)
ColorTransferCharacteristicGamma22 = ColorTransferCharacteristic(C.AVCOL_TRC_GAMMA22)
ColorTransferCharacteristicGamma28 = ColorTransferCharacteristic(C.AVCOL_TRC_GAMMA28)
ColorTransferCharacteristicSmpte170M = ColorTransferCharacteristic(C.AVCOL_TRC_SMPTE170M)
ColorTransferCharacteristicSmpte240M = ColorTransferCharacteristic(C.AVCOL_TRC_SMPTE240M)
ColorTransferCharacteristicLinear = ColorTransferCharacteristic(C.AVCOL_TRC_LINEAR)
ColorTransferCharacteristicLog = ColorTransferCharacteristic(C.AVCOL_TRC_LOG)
ColorTransferCharacteristicLogSqrt = ColorTransferCharacteristic(C.AVCOL_TRC_LOG_SQRT)
ColorTransferCharacteristicIec6196624 = ColorTransferCharacteristic(C.AVCOL_TRC_IEC61966_2_4)
ColorTransferCharacteristicBt1361Ecg = ColorTransferCharacteristic(C.AVCOL_TRC_BT1361_ECG)
ColorTransferCharacteristicIec6196621 = ColorTransferCharacteristic(C.AVCOL_TRC_IEC61966_2_1)
ColorTransferCharacteristicBt202010 = ColorTransferCharacteristic(C.AVCOL_TRC_BT2020_10)
ColorTransferCharacteristicBt202012 = ColorTransferCharacteristic(C.AVCOL_TRC_BT2020_12)
ColorTransferCharacteristicSmpte2084 = ColorTransferCharacteristic(C.AVCOL_TRC_SMPTE2084)
ColorTransferCharacteristicSmptest2084 = ColorTransferCharacteristic(C.AVCOL_TRC_SMPTEST2084)
ColorTransferCharacteristicSmpte428 = ColorTransferCharacteristic(C.AVCOL_TRC_SMPTE428)
ColorTransferCharacteristicSmptest4281 = ColorTransferCharacteristic(C.AVCOL_TRC_SMPTEST428_1)
ColorTransferCharacteristicAribStdB67 = ColorTransferCharacteristic(C.AVCOL_TRC_ARIB_STD_B67)
ColorTransferCharacteristicNb = ColorTransferCharacteristic(C.AVCOL_TRC_NB)
)

View File

@@ -1,9 +0,0 @@
package astiav
//#cgo pkg-config: libavdevice
//#include <libavdevice/avdevice.h>
import "C"
func RegisterAllDevices() {
C.avdevice_register_all()
}

View File

@@ -1,9 +0,0 @@
package astiav
import (
"testing"
)
func TestDevice(t *testing.T) {
RegisterAllDevices()
}

View File

@@ -1,73 +0,0 @@
package astiav
//#cgo pkg-config: libavcodec libavutil
//#include <libavcodec/avcodec.h>
//#include <libavutil/dict.h>
//#include <stdlib.h>
import "C"
import (
"unsafe"
)
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavutil/dict.h#L84
type Dictionary struct {
c *C.struct_AVDictionary
}
func NewDictionary() *Dictionary {
return &Dictionary{}
}
func newDictionaryFromC(c *C.struct_AVDictionary) *Dictionary {
if c == nil {
return nil
}
return &Dictionary{c: c}
}
func (d *Dictionary) Set(key, value string, flags DictionaryFlags) error {
ck := C.CString(key)
defer C.free(unsafe.Pointer(ck))
cv := C.CString(value)
defer C.free(unsafe.Pointer(cv))
return newError(C.av_dict_set(&d.c, ck, cv, C.int(flags)))
}
func (d *Dictionary) ParseString(i, keyValSep, pairsSep string, flags DictionaryFlags) error {
ci := C.CString(i)
defer C.free(unsafe.Pointer(ci))
ck := C.CString(keyValSep)
defer C.free(unsafe.Pointer(ck))
cp := C.CString(pairsSep)
defer C.free(unsafe.Pointer(cp))
return newError(C.av_dict_parse_string(&d.c, ci, ck, cp, C.int(flags)))
}
func (d *Dictionary) Get(key string, prev *DictionaryEntry, flags DictionaryFlags) *DictionaryEntry {
ck := C.CString(key)
defer C.free(unsafe.Pointer(ck))
var cp *C.struct_AVDictionaryEntry
if prev != nil {
cp = prev.c
}
if e := C.av_dict_get(d.c, ck, cp, C.int(flags)); e != nil {
return newDictionaryEntryFromC(e)
}
return nil
}
func (d *Dictionary) Free() {
C.av_dict_free(&d.c)
}
func (d *Dictionary) Pack() []byte {
return bytesFromC(func(size *C.size_t) *C.uint8_t {
return C.av_packet_pack_dictionary(d.c, size)
})
}
func (d *Dictionary) Unpack(b []byte) error {
return bytesToC(b, func(b *C.uint8_t, size C.size_t) error {
return newError(C.av_packet_unpack_dictionary(b, size, &d.c))
})
}

View File

@@ -1,22 +0,0 @@
package astiav
//#cgo pkg-config: libavutil
//#include <libavutil/dict.h>
import "C"
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavutil/dict.h#L79
type DictionaryEntry struct {
c *C.struct_AVDictionaryEntry
}
func newDictionaryEntryFromC(c *C.struct_AVDictionaryEntry) *DictionaryEntry {
return &DictionaryEntry{c: c}
}
func (e DictionaryEntry) Key() string {
return C.GoString(e.c.key)
}
func (e DictionaryEntry) Value() string {
return C.GoString(e.c.value)
}

View File

@@ -1,18 +0,0 @@
package astiav
//#cgo pkg-config: libavutil
//#include <libavutil/dict.h>
import "C"
type DictionaryFlag int64
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavutil/dict.h#L67
const (
DictionaryFlagMatchCase = DictionaryFlag(C.AV_DICT_MATCH_CASE)
DictionaryFlagIgnoreSuffix = DictionaryFlag(C.AV_DICT_IGNORE_SUFFIX)
DictionaryFlagDontStrdupKey = DictionaryFlag(C.AV_DICT_DONT_STRDUP_KEY)
DictionaryFlagDontStrdupVal = DictionaryFlag(C.AV_DICT_DONT_STRDUP_VAL)
DictionaryFlagDontOverwrite = DictionaryFlag(C.AV_DICT_DONT_OVERWRITE)
DictionaryFlagAppend = DictionaryFlag(C.AV_DICT_APPEND)
DictionaryFlagMultikey = DictionaryFlag(C.AV_DICT_MULTIKEY)
)

View File

@@ -1,42 +0,0 @@
package astiav
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestDictionary(t *testing.T) {
d := NewDictionary()
defer d.Free()
err := d.ParseString("invalid,,", ":", ",", 0)
require.Error(t, err)
err = d.ParseString("k1=v1,k2=v2", "=", ",", 0)
require.NoError(t, err)
err = d.Set("k3", "v3", 0)
require.NoError(t, err)
e := d.Get("k1", nil, 0)
require.NotNil(t, e)
require.Equal(t, "k1", e.Key())
require.Equal(t, "v1", e.Value())
e = d.Get("k2", nil, 0)
require.NotNil(t, e)
require.Equal(t, "k2", e.Key())
require.Equal(t, "v2", e.Value())
e = d.Get("k3", nil, 0)
require.NotNil(t, e)
require.Equal(t, "k3", e.Key())
require.Equal(t, "v3", e.Value())
e = d.Get("k4", nil, 0)
require.Nil(t, e)
b := d.Pack()
require.Equal(t, "k1\x00v1\x00k2\x00v2\x00k3\x00v3\x00", string(b))
err = d.Unpack([]byte("k4\x00v4\x00"))
require.NoError(t, err)
e = d.Get("k4", nil, 0)
require.NotNil(t, e)
require.Equal(t, "k4", e.Key())
require.Equal(t, "v4", e.Value())
}

View File

@@ -1,48 +0,0 @@
package astiav
//#cgo pkg-config: libavutil
//#include <libavutil/display.h>
import "C"
import (
"encoding/binary"
"fmt"
"unsafe"
)
type DisplayMatrix [9]uint32
func NewDisplayMatrixFromBytes(b []byte) (m *DisplayMatrix, err error) {
// Check length
if len(b) < 36 {
err = fmt.Errorf("astiav: invalid length %d < 36", len(b))
return
}
// Create display matrix
m = &DisplayMatrix{}
// Loop
for idx := 0; idx < 9; idx++ {
m[idx] = binary.LittleEndian.Uint32(b[idx*4 : (idx+1)*4])
}
return
}
func NewDisplayMatrixFromRotation(angle float64) *DisplayMatrix {
m := &DisplayMatrix{}
C.av_display_rotation_set((*C.int32_t)(unsafe.Pointer(&m[0])), C.double(angle))
return m
}
func (m DisplayMatrix) Bytes() []byte {
b := make([]byte, 0, 36)
for _, v := range m {
b = binary.LittleEndian.AppendUint32(b, v)
}
return b
}
// Rotation is a clockwise angle
func (m DisplayMatrix) Rotation() float64 {
return -float64(C.av_display_rotation_get((*C.int32_t)(unsafe.Pointer(&m[0]))))
}

View File

@@ -1,24 +0,0 @@
package astiav
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestDisplayMatrix(t *testing.T) {
_, err := NewDisplayMatrixFromBytes([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})
require.Error(t, err)
b := []byte{0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64}
dm, err := NewDisplayMatrixFromBytes(b)
require.NoError(t, err)
require.Equal(t, DisplayMatrix{0x0, 0xffff0000, 0x0, 0x10000, 0x0, 0x0, 0x0, 0x0, 0x40000000}, *dm)
require.Equal(t, -90.0, dm.Rotation())
require.Equal(t, b, dm.Bytes())
dm = NewDisplayMatrixFromRotation(-90)
require.Equal(t, -90.0, dm.Rotation())
dm, err = NewDisplayMatrixFromBytes([]byte{0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64})
require.NoError(t, err)
require.Equal(t, DisplayMatrix{0x0, 0x10000, 0x0, 0xffff0000, 0x0, 0x0, 0x0, 0x0, 0x40000000}, *dm)
require.Equal(t, 90.0, dm.Rotation())
}

View File

@@ -1,68 +0,0 @@
package astiav
//#cgo pkg-config: libavutil
//#include <libavutil/avutil.h>
//#include <errno.h>
import "C"
type Error int
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavutil/error.h#L51
const (
ErrBsfNotFound = Error(C.AVERROR_BSF_NOT_FOUND)
ErrBufferTooSmall = Error(C.AVERROR_BUFFER_TOO_SMALL)
ErrBug = Error(C.AVERROR_BUG)
ErrBug2 = Error(C.AVERROR_BUG2)
ErrDecoderNotFound = Error(C.AVERROR_DECODER_NOT_FOUND)
ErrDemuxerNotFound = Error(C.AVERROR_DEMUXER_NOT_FOUND)
ErrEagain = Error(-(C.EAGAIN))
ErrEio = Error(-(C.EIO))
ErrEncoderNotFound = Error(C.AVERROR_ENCODER_NOT_FOUND)
ErrEof = Error(C.AVERROR_EOF)
ErrEperm = Error(-(C.EPERM))
ErrEpipe = Error(-(C.EPIPE))
ErrEtimedout = Error(-(C.ETIMEDOUT))
ErrExit = Error(C.AVERROR_EXIT)
ErrExperimental = Error(C.AVERROR_EXPERIMENTAL)
ErrExternal = Error(C.AVERROR_EXTERNAL)
ErrFilterNotFound = Error(C.AVERROR_FILTER_NOT_FOUND)
ErrHttpBadRequest = Error(C.AVERROR_HTTP_BAD_REQUEST)
ErrHttpForbidden = Error(C.AVERROR_HTTP_FORBIDDEN)
ErrHttpNotFound = Error(C.AVERROR_HTTP_NOT_FOUND)
ErrHttpOther4Xx = Error(C.AVERROR_HTTP_OTHER_4XX)
ErrHttpServerError = Error(C.AVERROR_HTTP_SERVER_ERROR)
ErrHttpUnauthorized = Error(C.AVERROR_HTTP_UNAUTHORIZED)
ErrInputChanged = Error(C.AVERROR_INPUT_CHANGED)
ErrInvaliddata = Error(C.AVERROR_INVALIDDATA)
ErrMaxStringSize = Error(C.AV_ERROR_MAX_STRING_SIZE)
ErrMuxerNotFound = Error(C.AVERROR_MUXER_NOT_FOUND)
ErrOptionNotFound = Error(C.AVERROR_OPTION_NOT_FOUND)
ErrOutputChanged = Error(C.AVERROR_OUTPUT_CHANGED)
ErrPatchwelcome = Error(C.AVERROR_PATCHWELCOME)
ErrProtocolNotFound = Error(C.AVERROR_PROTOCOL_NOT_FOUND)
ErrStreamNotFound = Error(C.AVERROR_STREAM_NOT_FOUND)
ErrUnknown = Error(C.AVERROR_UNKNOWN)
)
func newError(ret C.int) error {
i := int(ret)
if i >= 0 {
return nil
}
return Error(i)
}
func (e Error) Error() string {
s, _ := stringFromC(255, func(buf *C.char, size C.size_t) error {
return newError(C.av_strerror(C.int(e), buf, size))
})
return s
}
func (e Error) Is(err error) bool {
a, ok := err.(Error)
if !ok {
return false
}
return int(a) == int(e)
}

View File

@@ -1,22 +0,0 @@
package astiav
import (
"errors"
"fmt"
"testing"
"github.com/stretchr/testify/require"
)
type testError struct{}
func (err testError) Error() string { return "" }
func TestError(t *testing.T) {
require.Equal(t, "Decoder not found", ErrDecoderNotFound.Error())
err1 := fmt.Errorf("test 1: %w", ErrDecoderNotFound)
require.True(t, errors.Is(err1, ErrDecoderNotFound))
require.False(t, errors.Is(err1, testError{}))
err2 := fmt.Errorf("test 2: %w", ErrDemuxerNotFound)
require.False(t, errors.Is(err2, ErrDecoderNotFound))
}

View File

@@ -1,146 +0,0 @@
package main
import (
"errors"
"flag"
"fmt"
"log"
"strings"
"github.com/asticode/go-astiav"
)
var (
input = flag.String("i", "", "the input path")
)
type stream struct {
decCodec *astiav.Codec
decCodecContext *astiav.CodecContext
inputStream *astiav.Stream
}
func main() {
// Handle ffmpeg logs
astiav.SetLogLevel(astiav.LogLevelDebug)
astiav.SetLogCallback(func(c astiav.Classer, l astiav.LogLevel, fmt, msg string) {
var cs string
if c != nil {
if cl := c.Class(); cl != nil {
cs = " - class: " + cl.String()
}
}
log.Printf("ffmpeg log: %s%s - level: %d\n", strings.TrimSpace(msg), cs, l)
})
// Parse flags
flag.Parse()
// Usage
if *input == "" {
log.Println("Usage: <binary path> -i <input path>")
return
}
// Alloc packet
pkt := astiav.AllocPacket()
defer pkt.Free()
// Alloc frame
f := astiav.AllocFrame()
defer f.Free()
// Alloc input format context
inputFormatContext := astiav.AllocFormatContext()
if inputFormatContext == nil {
log.Fatal(errors.New("main: input format context is nil"))
}
defer inputFormatContext.Free()
// Open input
if err := inputFormatContext.OpenInput(*input, nil, nil); err != nil {
log.Fatal(fmt.Errorf("main: opening input failed: %w", err))
}
defer inputFormatContext.CloseInput()
// Find stream info
if err := inputFormatContext.FindStreamInfo(nil); err != nil {
log.Fatal(fmt.Errorf("main: finding stream info failed: %w", err))
}
// Loop through streams
streams := make(map[int]*stream) // Indexed by input stream index
for _, is := range inputFormatContext.Streams() {
// Only process audio or video
if is.CodecParameters().MediaType() != astiav.MediaTypeAudio &&
is.CodecParameters().MediaType() != astiav.MediaTypeVideo {
continue
}
// Create stream
s := &stream{inputStream: is}
// Find decoder
if s.decCodec = astiav.FindDecoder(is.CodecParameters().CodecID()); s.decCodec == nil {
log.Fatal(errors.New("main: codec is nil"))
}
// Alloc codec context
if s.decCodecContext = astiav.AllocCodecContext(s.decCodec); s.decCodecContext == nil {
log.Fatal(errors.New("main: codec context is nil"))
}
defer s.decCodecContext.Free()
// Update codec context
if err := is.CodecParameters().ToCodecContext(s.decCodecContext); err != nil {
log.Fatal(fmt.Errorf("main: updating codec context failed: %w", err))
}
// Open codec context
if err := s.decCodecContext.Open(s.decCodec, nil); err != nil {
log.Fatal(fmt.Errorf("main: opening codec context failed: %w", err))
}
// Add stream
streams[is.Index()] = s
}
// Loop through packets
for {
// Read frame
if err := inputFormatContext.ReadFrame(pkt); err != nil {
if errors.Is(err, astiav.ErrEof) {
break
}
log.Fatal(fmt.Errorf("main: reading frame failed: %w", err))
}
// Get stream
s, ok := streams[pkt.StreamIndex()]
if !ok {
continue
}
// Send packet
if err := s.decCodecContext.SendPacket(pkt); err != nil {
log.Fatal(fmt.Errorf("main: sending packet failed: %w", err))
}
// Loop
for {
// Receive frame
if err := s.decCodecContext.ReceiveFrame(f); err != nil {
if errors.Is(err, astiav.ErrEof) || errors.Is(err, astiav.ErrEagain) {
break
}
log.Fatal(fmt.Errorf("main: receiving frame failed: %w", err))
}
// Do something with decoded frame
log.Printf("new frame: stream %d - pts: %d", pkt.StreamIndex(), f.Pts())
}
}
// Success
log.Println("success")
}

View File

@@ -1,305 +0,0 @@
package main
import (
"errors"
"flag"
"fmt"
"log"
"strconv"
"strings"
"github.com/asticode/go-astiav"
"github.com/asticode/go-astikit"
)
var (
input = flag.String("i", "", "the input path")
)
var (
c = astikit.NewCloser()
inputFormatContext *astiav.FormatContext
s *stream
)
type stream struct {
buffersinkContext *astiav.FilterContext
buffersrcContext *astiav.FilterContext
decCodec *astiav.Codec
decCodecContext *astiav.CodecContext
decFrame *astiav.Frame
filterFrame *astiav.Frame
filterGraph *astiav.FilterGraph
inputStream *astiav.Stream
lastPts int64
}
func main() {
// Handle ffmpeg logs
astiav.SetLogLevel(astiav.LogLevelDebug)
astiav.SetLogCallback(func(c astiav.Classer, l astiav.LogLevel, fmt, msg string) {
var cs string
if c != nil {
if cl := c.Class(); cl != nil {
cs = " - class: " + cl.String()
}
}
log.Printf("ffmpeg log: %s%s - level: %d\n", strings.TrimSpace(msg), cs, l)
})
// Parse flags
flag.Parse()
// Usage
if *input == "" {
log.Println("Usage: <binary path> -i <input path>")
return
}
// We use an astikit.Closer to free all resources properly
defer c.Close()
// Open input file
if err := openInputFile(); err != nil {
log.Fatal(fmt.Errorf("main: opening input file failed: %w", err))
}
// Init filter
if err := initFilter(); err != nil {
log.Fatal(fmt.Errorf("main: initializing filter failed: %w", err))
}
// Alloc packet
pkt := astiav.AllocPacket()
c.Add(pkt.Free)
// Loop through packets
for {
// Read frame
if err := inputFormatContext.ReadFrame(pkt); err != nil {
if errors.Is(err, astiav.ErrEof) {
break
}
log.Fatal(fmt.Errorf("main: reading frame failed: %w", err))
}
// Invalid stream
if pkt.StreamIndex() != s.inputStream.Index() {
continue
}
// Send packet
if err := s.decCodecContext.SendPacket(pkt); err != nil {
log.Fatal(fmt.Errorf("main: sending packet failed: %w", err))
}
// Loop
for {
// Receive frame
if err := s.decCodecContext.ReceiveFrame(s.decFrame); err != nil {
if errors.Is(err, astiav.ErrEof) || errors.Is(err, astiav.ErrEagain) {
break
}
log.Fatal(fmt.Errorf("main: receiving frame failed: %w", err))
}
// Filter frame
if err := filterFrame(s.decFrame, s); err != nil {
log.Fatal(fmt.Errorf("main: filtering frame failed: %w", err))
}
}
}
// Flush filter
if err := filterFrame(nil, s); err != nil {
log.Fatal(fmt.Errorf("main: filtering frame failed: %w", err))
}
// Success
log.Println("success")
}
func openInputFile() (err error) {
// Alloc input format context
if inputFormatContext = astiav.AllocFormatContext(); inputFormatContext == nil {
err = errors.New("main: input format context is nil")
return
}
c.Add(inputFormatContext.Free)
// Open input
if err = inputFormatContext.OpenInput(*input, nil, nil); err != nil {
err = fmt.Errorf("main: opening input failed: %w", err)
return
}
c.Add(inputFormatContext.CloseInput)
// Find stream info
if err = inputFormatContext.FindStreamInfo(nil); err != nil {
err = fmt.Errorf("main: finding stream info failed: %w", err)
return
}
// Loop through streams
for _, is := range inputFormatContext.Streams() {
// Only process video
if is.CodecParameters().MediaType() != astiav.MediaTypeVideo {
continue
}
// Create stream
s = &stream{
inputStream: is,
lastPts: astiav.NoPtsValue,
}
// Find decoder
if s.decCodec = astiav.FindDecoder(is.CodecParameters().CodecID()); s.decCodec == nil {
err = errors.New("main: codec is nil")
return
}
// Alloc codec context
if s.decCodecContext = astiav.AllocCodecContext(s.decCodec); s.decCodecContext == nil {
err = errors.New("main: codec context is nil")
return
}
c.Add(s.decCodecContext.Free)
// Update codec context
if err = is.CodecParameters().ToCodecContext(s.decCodecContext); err != nil {
err = fmt.Errorf("main: updating codec context failed: %w", err)
return
}
// Open codec context
if err = s.decCodecContext.Open(s.decCodec, nil); err != nil {
err = fmt.Errorf("main: opening codec context failed: %w", err)
return
}
// Alloc frame
s.decFrame = astiav.AllocFrame()
c.Add(s.decFrame.Free)
break
}
// No video stream
if s == nil {
err = errors.New("main: no video stream")
return
}
return
}
func initFilter() (err error) {
// Alloc graph
if s.filterGraph = astiav.AllocFilterGraph(); s.filterGraph == nil {
err = errors.New("main: graph is nil")
return
}
c.Add(s.filterGraph.Free)
// Alloc outputs
outputs := astiav.AllocFilterInOut()
if outputs == nil {
err = errors.New("main: outputs is nil")
return
}
c.Add(outputs.Free)
// Alloc inputs
inputs := astiav.AllocFilterInOut()
if inputs == nil {
err = errors.New("main: inputs is nil")
return
}
c.Add(inputs.Free)
// Create buffersrc
buffersrc := astiav.FindFilterByName("buffer")
if buffersrc == nil {
err = errors.New("main: buffersrc is nil")
return
}
// Create buffersink
buffersink := astiav.FindFilterByName("buffersink")
if buffersink == nil {
err = errors.New("main: buffersink is nil")
return
}
// Create filter contexts
if s.buffersrcContext, err = s.filterGraph.NewFilterContext(buffersrc, "in", astiav.FilterArgs{
"pix_fmt": strconv.Itoa(int(s.decCodecContext.PixelFormat())),
"pixel_aspect": s.decCodecContext.SampleAspectRatio().String(),
"time_base": s.inputStream.TimeBase().String(),
"video_size": strconv.Itoa(s.decCodecContext.Width()) + "x" + strconv.Itoa(s.decCodecContext.Height()),
}); err != nil {
err = fmt.Errorf("main: creating buffersrc context failed: %w", err)
return
}
if s.buffersinkContext, err = s.filterGraph.NewFilterContext(buffersink, "in", nil); err != nil {
err = fmt.Errorf("main: creating buffersink context failed: %w", err)
return
}
// Update outputs
outputs.SetName("in")
outputs.SetFilterContext(s.buffersrcContext)
outputs.SetPadIdx(0)
outputs.SetNext(nil)
// Update inputs
inputs.SetName("out")
inputs.SetFilterContext(s.buffersinkContext)
inputs.SetPadIdx(0)
inputs.SetNext(nil)
// Parse
if err = s.filterGraph.Parse("transpose=cclock", inputs, outputs); err != nil {
err = fmt.Errorf("main: parsing filter failed: %w", err)
return
}
// Configure
if err = s.filterGraph.Configure(); err != nil {
err = fmt.Errorf("main: configuring filter failed: %w", err)
return
}
// Alloc frame
s.filterFrame = astiav.AllocFrame()
c.Add(s.filterFrame.Free)
return
}
func filterFrame(f *astiav.Frame, s *stream) (err error) {
// Add frame
if err = s.buffersrcContext.BuffersrcAddFrame(f, astiav.NewBuffersrcFlags(astiav.BuffersrcFlagKeepRef)); err != nil {
err = fmt.Errorf("main: adding frame failed: %w", err)
return
}
// Loop
for {
// Unref frame
s.filterFrame.Unref()
// Get frame
if err = s.buffersinkContext.BuffersinkGetFrame(s.filterFrame, astiav.NewBuffersinkFlags()); err != nil {
if errors.Is(err, astiav.ErrEof) || errors.Is(err, astiav.ErrEagain) {
err = nil
break
}
err = fmt.Errorf("main: getting frame failed: %w", err)
return
}
// Do something with filtered frame
log.Printf("new filtered frame: %dx%d\n", s.filterFrame.Width(), s.filterFrame.Height())
}
return
}

View File

@@ -1,217 +0,0 @@
package main
import (
"errors"
"flag"
"fmt"
"log"
"strings"
"github.com/asticode/go-astiav"
)
var (
decoderCodecName = flag.String("c", "", "the decoder codec name (e.g. h264_cuvid)")
hardwareDeviceName = flag.String("n", "", "the hardware device name (e.g. 0)")
hardwareDeviceTypeName = flag.String("t", "", "the hardware device type (e.g. cuda)")
input = flag.String("i", "", "the input path")
)
type stream struct {
decCodec *astiav.Codec
decCodecContext *astiav.CodecContext
hardwareDeviceContext *astiav.HardwareDeviceContext
hardwarePixelFormat astiav.PixelFormat
inputStream *astiav.Stream
}
func main() {
// Handle ffmpeg logs
astiav.SetLogLevel(astiav.LogLevelDebug)
astiav.SetLogCallback(func(c astiav.Classer, l astiav.LogLevel, fmt, msg string) {
var cs string
if c != nil {
if cl := c.Class(); cl != nil {
cs = " - class: " + cl.String()
}
}
log.Printf("ffmpeg log: %s%s - level: %d\n", strings.TrimSpace(msg), cs, l)
})
// Parse flags
flag.Parse()
// Usage
if *input == "" || *hardwareDeviceTypeName == "" {
log.Println("Usage: <binary path> -t <hardware device type> -i <input path> [-n <hardware device name> -c <decoder codec>]")
return
}
// Get hardware device type
hardwareDeviceType := astiav.FindHardwareDeviceTypeByName(*hardwareDeviceTypeName)
if hardwareDeviceType == astiav.HardwareDeviceTypeNone {
log.Fatal(errors.New("main: hardware device not found"))
}
// Alloc packet
pkt := astiav.AllocPacket()
defer pkt.Free()
// Alloc hardware frame
hardwareFrame := astiav.AllocFrame()
defer hardwareFrame.Free()
// Alloc software frame
softwareFrame := astiav.AllocFrame()
defer softwareFrame.Free()
// Alloc input format context
inputFormatContext := astiav.AllocFormatContext()
if inputFormatContext == nil {
log.Fatal(errors.New("main: input format context is nil"))
}
defer inputFormatContext.Free()
// Open input
if err := inputFormatContext.OpenInput(*input, nil, nil); err != nil {
log.Fatal(fmt.Errorf("main: opening input failed: %w", err))
}
defer inputFormatContext.CloseInput()
// Find stream info
if err := inputFormatContext.FindStreamInfo(nil); err != nil {
log.Fatal(fmt.Errorf("main: finding stream info failed: %w", err))
}
// Loop through streams
streams := make(map[int]*stream) // Indexed by input stream index
for _, is := range inputFormatContext.Streams() {
// Only process video
if is.CodecParameters().MediaType() != astiav.MediaTypeVideo {
continue
}
// Create stream
s := &stream{inputStream: is}
// Find decoder
if *decoderCodecName != "" {
s.decCodec = astiav.FindDecoderByName(*decoderCodecName)
} else {
s.decCodec = astiav.FindDecoder(is.CodecParameters().CodecID())
}
// No codec
if s.decCodec == nil {
log.Fatal(errors.New("main: codec is nil"))
}
// Alloc codec context
if s.decCodecContext = astiav.AllocCodecContext(s.decCodec); s.decCodecContext == nil {
log.Fatal(errors.New("main: codec context is nil"))
}
defer s.decCodecContext.Free()
// Loop through codec hardware configs
for _, p := range s.decCodec.HardwareConfigs() {
// Valid hardware config
if p.MethodFlags().Has(astiav.CodecHardwareConfigMethodFlagHwDeviceCtx) && p.HardwareDeviceType() == hardwareDeviceType {
s.hardwarePixelFormat = p.PixelFormat()
break
}
}
// No valid hardware pixel format
if s.hardwarePixelFormat == astiav.PixelFormatNone {
log.Fatal(errors.New("main: hardware device type not supported by decoder"))
}
// Update codec context
if err := is.CodecParameters().ToCodecContext(s.decCodecContext); err != nil {
log.Fatal(fmt.Errorf("main: updating codec context failed: %w", err))
}
// Create hardware device context
var err error
if s.hardwareDeviceContext, err = astiav.CreateHardwareDeviceContext(hardwareDeviceType, *hardwareDeviceName, nil); err != nil {
log.Fatal(fmt.Errorf("main: creating hardware device context failed: %w", err))
}
// Update decoder context
s.decCodecContext.SetHardwareDeviceContext(s.hardwareDeviceContext)
s.decCodecContext.SetPixelFormatCallback(func(pfs []astiav.PixelFormat) astiav.PixelFormat {
for _, pf := range pfs {
if pf == s.hardwarePixelFormat {
return pf
}
}
log.Fatal(errors.New("main: using hardware pixel format failed"))
return astiav.PixelFormatNone
})
// Open codec context
if err := s.decCodecContext.Open(s.decCodec, nil); err != nil {
log.Fatal(fmt.Errorf("main: opening codec context failed: %w", err))
}
// Add stream
streams[is.Index()] = s
}
// Loop through packets
for {
// Read frame
if err := inputFormatContext.ReadFrame(pkt); err != nil {
if errors.Is(err, astiav.ErrEof) {
break
}
log.Fatal(fmt.Errorf("main: reading frame failed: %w", err))
}
// Get stream
s, ok := streams[pkt.StreamIndex()]
if !ok {
continue
}
// Send packet
if err := s.decCodecContext.SendPacket(pkt); err != nil {
log.Fatal(fmt.Errorf("main: sending packet failed: %w", err))
}
// Loop
for {
// Receive frame
if err := s.decCodecContext.ReceiveFrame(hardwareFrame); err != nil {
if errors.Is(err, astiav.ErrEof) || errors.Is(err, astiav.ErrEagain) {
break
}
log.Fatal(fmt.Errorf("main: receiving frame failed: %w", err))
}
// Get final frame
var finalFrame *astiav.Frame
if hardwareFrame.PixelFormat() == s.hardwarePixelFormat {
// Transfer hardware data
if err := hardwareFrame.TransferHardwareData(softwareFrame); err != nil {
log.Fatal(fmt.Errorf("main: transferring hardware data failed: %w", err))
}
// Update pts
softwareFrame.SetPts(hardwareFrame.Pts())
// Update final frame
finalFrame = softwareFrame
} else {
// Update final frame
finalFrame = hardwareFrame
}
// Do something with decoded frame
log.Printf("new frame: stream %d - pts: %d - transferred: %v", pkt.StreamIndex(), finalFrame.Pts(), hardwareFrame.PixelFormat() == s.hardwarePixelFormat)
}
}
// Success
log.Println("success")
}

View File

@@ -1,163 +0,0 @@
package main
import (
"errors"
"flag"
"fmt"
"log"
"strings"
"github.com/asticode/go-astiav"
)
var (
input = flag.String("i", "", "the input path")
output = flag.String("o", "", "the output path")
)
func main() {
// Handle ffmpeg logs
astiav.SetLogLevel(astiav.LogLevelDebug)
astiav.SetLogCallback(func(c astiav.Classer, l astiav.LogLevel, fmt, msg string) {
var cs string
if c != nil {
if cl := c.Class(); cl != nil {
cs = " - class: " + cl.String()
}
}
log.Printf("ffmpeg log: %s%s - level: %d\n", strings.TrimSpace(msg), cs, l)
})
// Parse flags
flag.Parse()
// Usage
if *input == "" || *output == "" {
log.Println("Usage: <binary path> -i <input path> -o <output path>")
return
}
// Alloc packet
pkt := astiav.AllocPacket()
defer pkt.Free()
// Alloc input format context
inputFormatContext := astiav.AllocFormatContext()
if inputFormatContext == nil {
log.Fatal(errors.New("main: input format context is nil"))
}
defer inputFormatContext.Free()
// Open input
if err := inputFormatContext.OpenInput(*input, nil, nil); err != nil {
log.Fatal(fmt.Errorf("main: opening input failed: %w", err))
}
defer inputFormatContext.CloseInput()
// Find stream info
if err := inputFormatContext.FindStreamInfo(nil); err != nil {
log.Fatal(fmt.Errorf("main: finding stream info failed: %w", err))
}
// Alloc output format context
outputFormatContext, err := astiav.AllocOutputFormatContext(nil, "", *output)
if err != nil {
log.Fatal(fmt.Errorf("main: allocating output format context failed: %w", err))
}
if outputFormatContext == nil {
log.Fatal(errors.New("main: output format context is nil"))
}
defer outputFormatContext.Free()
// Loop through streams
inputStreams := make(map[int]*astiav.Stream) // Indexed by input stream index
outputStreams := make(map[int]*astiav.Stream) // Indexed by input stream index
for _, is := range inputFormatContext.Streams() {
// Only process audio or video
if is.CodecParameters().MediaType() != astiav.MediaTypeAudio &&
is.CodecParameters().MediaType() != astiav.MediaTypeVideo {
continue
}
// Add input stream
inputStreams[is.Index()] = is
// Add stream to output format context
os := outputFormatContext.NewStream(nil)
if os == nil {
log.Fatal(errors.New("main: output stream is nil"))
}
// Copy codec parameters
if err = is.CodecParameters().Copy(os.CodecParameters()); err != nil {
log.Fatal(fmt.Errorf("main: copying codec parameters failed: %w", err))
}
// Reset codec tag
os.CodecParameters().SetCodecTag(0)
// Add output stream
outputStreams[is.Index()] = os
}
// If this is a file, we need to use an io context
if !outputFormatContext.OutputFormat().Flags().Has(astiav.IOFormatFlagNofile) {
// Open io context
ioContext, err := astiav.OpenIOContext(*output, astiav.NewIOContextFlags(astiav.IOContextFlagWrite))
if err != nil {
log.Fatal(fmt.Errorf("main: opening io context failed: %w", err))
}
defer ioContext.Close() //nolint:errcheck
// Update output format context
outputFormatContext.SetPb(ioContext)
}
// Write header
if err = outputFormatContext.WriteHeader(nil); err != nil {
log.Fatal(fmt.Errorf("main: writing header failed: %w", err))
}
// Loop through packets
for {
// Read frame
if err = inputFormatContext.ReadFrame(pkt); err != nil {
if errors.Is(err, astiav.ErrEof) {
break
}
log.Fatal(fmt.Errorf("main: reading frame failed: %w", err))
}
// Get input stream
inputStream, ok := inputStreams[pkt.StreamIndex()]
if !ok {
pkt.Unref()
continue
}
// Get output stream
outputStream, ok := outputStreams[pkt.StreamIndex()]
if !ok {
pkt.Unref()
continue
}
// Update packet
pkt.SetStreamIndex(outputStream.Index())
pkt.RescaleTs(inputStream.TimeBase(), outputStream.TimeBase())
pkt.SetPos(-1)
// Write frame
if err = outputFormatContext.WriteInterleavedFrame(pkt); err != nil {
log.Fatal(fmt.Errorf("main: writing interleaved frame failed: %w", err))
}
}
// Write trailer
if err = outputFormatContext.WriteTrailer(); err != nil {
log.Fatal(fmt.Errorf("main: writing trailer failed: %w", err))
}
// Success
log.Println("success")
}

View File

@@ -1,104 +0,0 @@
package main
import (
"flag"
"fmt"
"image/png"
"log"
"os"
"strings"
"github.com/asticode/go-astiav"
)
var (
output = flag.String("o", "", "the png output path")
dstWidth = flag.Int("w", 50, "destination width")
dstHeight = flag.Int("h", 50, "destination height")
)
func main() {
// Handle ffmpeg logs
astiav.SetLogLevel(astiav.LogLevelDebug)
astiav.SetLogCallback(func(c astiav.Classer, l astiav.LogLevel, fmt, msg string) {
var cs string
if c != nil {
if cl := c.Class(); cl != nil {
cs = " - class: " + cl.String()
}
}
log.Printf("ffmpeg log: %s%s - level: %d\n", strings.TrimSpace(msg), cs, l)
})
// Parse flags
flag.Parse()
// Usage
if *output == "" || *dstWidth <= 0 || *dstHeight <= 0 {
log.Println("Usage: <binary path> -o <output path> -w <output width> -h <output height>")
return
}
// Create destination file
dstFile, err := os.Create(*output)
if err != nil {
log.Fatal(fmt.Errorf("main: creating %s failed: %w", *output, err))
}
defer dstFile.Close()
// Create source frame
srcFrame := astiav.AllocFrame()
defer srcFrame.Free()
srcFrame.SetWidth(320)
srcFrame.SetHeight(240)
srcFrame.SetPixelFormat(astiav.PixelFormatYuv420P)
if err = srcFrame.AllocBuffer(1); err != nil {
log.Fatal(fmt.Errorf("main: allocating source frame buffer failed: %w", err))
}
if err = srcFrame.ImageFillBlack(); err != nil {
log.Fatal(fmt.Errorf("main: filling source frame with black image failed: %w", err))
}
// Create destination frame
dstFrame := astiav.AllocFrame()
defer dstFrame.Free()
// Create software scale context
swsCtx, err := astiav.CreateSoftwareScaleContext(
srcFrame.Width(),
srcFrame.Height(),
srcFrame.PixelFormat(),
*dstWidth,
*dstHeight,
astiav.PixelFormatRgba,
astiav.NewSoftwareScaleContextFlags(astiav.SoftwareScaleContextFlagBilinear),
)
if err != nil {
log.Fatal(fmt.Errorf("main: creating software scale context failed: %w", err))
}
defer swsCtx.Free()
// Scale frame
if err := swsCtx.ScaleFrame(srcFrame, dstFrame); err != nil {
log.Fatal(fmt.Errorf("main: scaling frame failed: %w", err))
}
// Guess destination image format
img, err := dstFrame.Data().GuessImageFormat()
if err != nil {
log.Fatal(fmt.Errorf("main: guessing destination image format failed: %w", err))
}
// Copy frame data to destination image
if err = dstFrame.Data().ToImage(img); err != nil {
log.Fatal(fmt.Errorf("main: copying frame data to destination image failed: %w", err))
}
// Encode to png
if err = png.Encode(dstFile, img); err != nil {
log.Fatal(fmt.Errorf("main: encoding to png failed: %w", err))
}
// Success
log.Println("success")
}

View File

@@ -1,510 +0,0 @@
package main
import (
"errors"
"flag"
"fmt"
"log"
"strconv"
"strings"
"github.com/asticode/go-astiav"
"github.com/asticode/go-astikit"
)
var (
input = flag.String("i", "", "the input path")
output = flag.String("o", "", "the output path")
)
var (
c = astikit.NewCloser()
inputFormatContext *astiav.FormatContext
outputFormatContext *astiav.FormatContext
streams = make(map[int]*stream) // Indexed by input stream index
)
type stream struct {
buffersinkContext *astiav.FilterContext
buffersrcContext *astiav.FilterContext
decCodec *astiav.Codec
decCodecContext *astiav.CodecContext
decFrame *astiav.Frame
encCodec *astiav.Codec
encCodecContext *astiav.CodecContext
encPkt *astiav.Packet
filterFrame *astiav.Frame
filterGraph *astiav.FilterGraph
inputStream *astiav.Stream
outputStream *astiav.Stream
}
func main() {
// Handle ffmpeg logs
astiav.SetLogLevel(astiav.LogLevelDebug)
astiav.SetLogCallback(func(c astiav.Classer, l astiav.LogLevel, fmt, msg string) {
var cs string
if c != nil {
if cl := c.Class(); cl != nil {
cs = " - class: " + cl.String()
}
}
log.Printf("ffmpeg log: %s%s - level: %d\n", strings.TrimSpace(msg), cs, l)
})
// Parse flags
flag.Parse()
// Usage
if *input == "" || *output == "" {
log.Println("Usage: <binary path> -i <input path> -o <output path>")
return
}
// We use an astikit.Closer to free all resources properly
defer c.Close()
// Open input file
if err := openInputFile(); err != nil {
log.Fatal(fmt.Errorf("main: opening input file failed: %w", err))
}
// Open output file
if err := openOutputFile(); err != nil {
log.Fatal(fmt.Errorf("main: opening output file failed: %w", err))
}
// Init filters
if err := initFilters(); err != nil {
log.Fatal(fmt.Errorf("main: initializing filters failed: %w", err))
}
// Alloc packet
pkt := astiav.AllocPacket()
c.Add(pkt.Free)
// Loop through packets
for {
// Read frame
if err := inputFormatContext.ReadFrame(pkt); err != nil {
if errors.Is(err, astiav.ErrEof) {
break
}
log.Fatal(fmt.Errorf("main: reading frame failed: %w", err))
}
// Get stream
s, ok := streams[pkt.StreamIndex()]
if !ok {
continue
}
// Update packet
pkt.RescaleTs(s.inputStream.TimeBase(), s.decCodecContext.TimeBase())
// Send packet
if err := s.decCodecContext.SendPacket(pkt); err != nil {
log.Fatal(fmt.Errorf("main: sending packet failed: %w", err))
}
// Loop
for {
// Receive frame
if err := s.decCodecContext.ReceiveFrame(s.decFrame); err != nil {
if errors.Is(err, astiav.ErrEof) || errors.Is(err, astiav.ErrEagain) {
break
}
log.Fatal(fmt.Errorf("main: receiving frame failed: %w", err))
}
// Filter, encode and write frame
if err := filterEncodeWriteFrame(s.decFrame, s); err != nil {
log.Fatal(fmt.Errorf("main: filtering, encoding and writing frame failed: %w", err))
}
}
}
// Loop through streams
for _, s := range streams {
// Flush filter
if err := filterEncodeWriteFrame(nil, s); err != nil {
log.Fatal(fmt.Errorf("main: filtering, encoding and writing frame failed: %w", err))
}
// Flush encoder
if err := encodeWriteFrame(nil, s); err != nil {
log.Fatal(fmt.Errorf("main: encoding and writing frame failed: %w", err))
}
}
// Write trailer
if err := outputFormatContext.WriteTrailer(); err != nil {
log.Fatal(fmt.Errorf("main: writing trailer failed: %w", err))
}
// Success
log.Println("success")
}
func openInputFile() (err error) {
// Alloc input format context
if inputFormatContext = astiav.AllocFormatContext(); inputFormatContext == nil {
err = errors.New("main: input format context is nil")
return
}
c.Add(inputFormatContext.Free)
// Open input
if err = inputFormatContext.OpenInput(*input, nil, nil); err != nil {
err = fmt.Errorf("main: opening input failed: %w", err)
return
}
c.Add(inputFormatContext.CloseInput)
// Find stream info
if err = inputFormatContext.FindStreamInfo(nil); err != nil {
err = fmt.Errorf("main: finding stream info failed: %w", err)
return
}
// Loop through streams
for _, is := range inputFormatContext.Streams() {
// Only process audio or video
if is.CodecParameters().MediaType() != astiav.MediaTypeAudio &&
is.CodecParameters().MediaType() != astiav.MediaTypeVideo {
continue
}
// Create stream
s := &stream{inputStream: is}
// Find decoder
if s.decCodec = astiav.FindDecoder(is.CodecParameters().CodecID()); s.decCodec == nil {
err = errors.New("main: codec is nil")
return
}
// Alloc codec context
if s.decCodecContext = astiav.AllocCodecContext(s.decCodec); s.decCodecContext == nil {
err = errors.New("main: codec context is nil")
return
}
c.Add(s.decCodecContext.Free)
// Update codec context
if err = is.CodecParameters().ToCodecContext(s.decCodecContext); err != nil {
err = fmt.Errorf("main: updating codec context failed: %w", err)
return
}
// Set framerate
if is.CodecParameters().MediaType() == astiav.MediaTypeVideo {
s.decCodecContext.SetFramerate(inputFormatContext.GuessFrameRate(is, nil))
}
// Open codec context
if err = s.decCodecContext.Open(s.decCodec, nil); err != nil {
err = fmt.Errorf("main: opening codec context failed: %w", err)
return
}
// Alloc frame
s.decFrame = astiav.AllocFrame()
c.Add(s.decFrame.Free)
// Store stream
streams[is.Index()] = s
}
return
}
func openOutputFile() (err error) {
// Alloc output format context
if outputFormatContext, err = astiav.AllocOutputFormatContext(nil, "", *output); err != nil {
err = fmt.Errorf("main: allocating output format context failed: %w", err)
return
} else if outputFormatContext == nil {
err = errors.New("main: output format context is nil")
return
}
c.Add(outputFormatContext.Free)
// Loop through streams
for _, is := range inputFormatContext.Streams() {
// Get stream
s, ok := streams[is.Index()]
if !ok {
continue
}
// Create output stream
if s.outputStream = outputFormatContext.NewStream(nil); s.outputStream == nil {
err = errors.New("main: output stream is nil")
return
}
// Get codec id
codecID := astiav.CodecIDMpeg4
if s.decCodecContext.MediaType() == astiav.MediaTypeAudio {
codecID = astiav.CodecIDAac
}
// Find encoder
if s.encCodec = astiav.FindEncoder(codecID); s.encCodec == nil {
err = errors.New("main: codec is nil")
return
}
// Alloc codec context
if s.encCodecContext = astiav.AllocCodecContext(s.encCodec); s.encCodecContext == nil {
err = errors.New("main: codec context is nil")
return
}
c.Add(s.encCodecContext.Free)
// Update codec context
if s.decCodecContext.MediaType() == astiav.MediaTypeAudio {
if v := s.encCodec.ChannelLayouts(); len(v) > 0 {
s.encCodecContext.SetChannelLayout(v[0])
} else {
s.encCodecContext.SetChannelLayout(s.decCodecContext.ChannelLayout())
}
s.encCodecContext.SetChannels(s.decCodecContext.Channels())
s.encCodecContext.SetSampleRate(s.decCodecContext.SampleRate())
if v := s.encCodec.SampleFormats(); len(v) > 0 {
s.encCodecContext.SetSampleFormat(v[0])
} else {
s.encCodecContext.SetSampleFormat(s.decCodecContext.SampleFormat())
}
s.encCodecContext.SetTimeBase(s.decCodecContext.TimeBase())
} else {
s.encCodecContext.SetHeight(s.decCodecContext.Height())
if v := s.encCodec.PixelFormats(); len(v) > 0 {
s.encCodecContext.SetPixelFormat(v[0])
} else {
s.encCodecContext.SetPixelFormat(s.decCodecContext.PixelFormat())
}
s.encCodecContext.SetSampleAspectRatio(s.decCodecContext.SampleAspectRatio())
s.encCodecContext.SetTimeBase(s.decCodecContext.TimeBase())
s.encCodecContext.SetWidth(s.decCodecContext.Width())
}
// Update flags
if s.decCodecContext.Flags().Has(astiav.CodecContextFlagGlobalHeader) {
s.encCodecContext.SetFlags(s.encCodecContext.Flags().Add(astiav.CodecContextFlagGlobalHeader))
}
// Open codec context
if err = s.encCodecContext.Open(s.encCodec, nil); err != nil {
err = fmt.Errorf("main: opening codec context failed: %w", err)
return
}
// Update codec parameters
if err = s.outputStream.CodecParameters().FromCodecContext(s.encCodecContext); err != nil {
err = fmt.Errorf("main: updating codec parameters failed: %w", err)
return
}
// Update stream
s.outputStream.SetTimeBase(s.encCodecContext.TimeBase())
}
// If this is a file, we need to use an io context
if !outputFormatContext.OutputFormat().Flags().Has(astiav.IOFormatFlagNofile) {
// Open io context
var ioContext *astiav.IOContext
if ioContext, err = astiav.OpenIOContext(*output, astiav.NewIOContextFlags(astiav.IOContextFlagWrite)); err != nil {
err = fmt.Errorf("main: opening io context failed: %w", err)
return
}
c.AddWithError(ioContext.Close)
// Update output format context
outputFormatContext.SetPb(ioContext)
}
// Write header
if err = outputFormatContext.WriteHeader(nil); err != nil {
err = fmt.Errorf("main: writing header failed: %w", err)
return
}
return
}
func initFilters() (err error) {
// Loop through output streams
for _, s := range streams {
// Alloc graph
if s.filterGraph = astiav.AllocFilterGraph(); s.filterGraph == nil {
err = errors.New("main: graph is nil")
return
}
c.Add(s.filterGraph.Free)
// Alloc outputs
outputs := astiav.AllocFilterInOut()
if outputs == nil {
err = errors.New("main: outputs is nil")
return
}
c.Add(outputs.Free)
// Alloc inputs
inputs := astiav.AllocFilterInOut()
if inputs == nil {
err = errors.New("main: inputs is nil")
return
}
c.Add(inputs.Free)
// Switch on media type
var args astiav.FilterArgs
var buffersrc, buffersink *astiav.Filter
var content string
switch s.decCodecContext.MediaType() {
case astiav.MediaTypeAudio:
args = astiav.FilterArgs{
"channel_layout": s.decCodecContext.ChannelLayout().String(),
"sample_fmt": s.decCodecContext.SampleFormat().Name(),
"sample_rate": strconv.Itoa(s.decCodecContext.SampleRate()),
"time_base": s.decCodecContext.TimeBase().String(),
}
buffersrc = astiav.FindFilterByName("abuffer")
buffersink = astiav.FindFilterByName("abuffersink")
content = fmt.Sprintf("aformat=sample_fmts=%s:channel_layouts=%s", s.encCodecContext.SampleFormat().Name(), s.encCodecContext.ChannelLayout().String())
default:
args = astiav.FilterArgs{
"pix_fmt": strconv.Itoa(int(s.decCodecContext.PixelFormat())),
"pixel_aspect": s.decCodecContext.SampleAspectRatio().String(),
"time_base": s.decCodecContext.TimeBase().String(),
"video_size": strconv.Itoa(s.decCodecContext.Width()) + "x" + strconv.Itoa(s.decCodecContext.Height()),
}
buffersrc = astiav.FindFilterByName("buffer")
buffersink = astiav.FindFilterByName("buffersink")
content = fmt.Sprintf("format=pix_fmts=%s", s.encCodecContext.PixelFormat().Name())
}
// Check filters
if buffersrc == nil {
err = errors.New("main: buffersrc is nil")
return
}
if buffersink == nil {
err = errors.New("main: buffersink is nil")
return
}
// Create filter contexts
if s.buffersrcContext, err = s.filterGraph.NewFilterContext(buffersrc, "in", args); err != nil {
err = fmt.Errorf("main: creating buffersrc context failed: %w", err)
return
}
if s.buffersinkContext, err = s.filterGraph.NewFilterContext(buffersink, "out", nil); err != nil {
err = fmt.Errorf("main: creating buffersink context failed: %w", err)
return
}
// Update outputs
outputs.SetName("in")
outputs.SetFilterContext(s.buffersrcContext)
outputs.SetPadIdx(0)
outputs.SetNext(nil)
// Update inputs
inputs.SetName("out")
inputs.SetFilterContext(s.buffersinkContext)
inputs.SetPadIdx(0)
inputs.SetNext(nil)
// Parse
if err = s.filterGraph.Parse(content, inputs, outputs); err != nil {
err = fmt.Errorf("main: parsing filter failed: %w", err)
return
}
// Configure
if err = s.filterGraph.Configure(); err != nil {
err = fmt.Errorf("main: configuring filter failed: %w", err)
return
}
// Alloc frame
s.filterFrame = astiav.AllocFrame()
c.Add(s.filterFrame.Free)
// Alloc packet
s.encPkt = astiav.AllocPacket()
c.Add(s.encPkt.Free)
}
return
}
func filterEncodeWriteFrame(f *astiav.Frame, s *stream) (err error) {
// Add frame
if err = s.buffersrcContext.BuffersrcAddFrame(f, astiav.NewBuffersrcFlags(astiav.BuffersrcFlagKeepRef)); err != nil {
err = fmt.Errorf("main: adding frame failed: %w", err)
return
}
// Loop
for {
// Unref frame
s.filterFrame.Unref()
// Get frame
if err = s.buffersinkContext.BuffersinkGetFrame(s.filterFrame, astiav.NewBuffersinkFlags()); err != nil {
if errors.Is(err, astiav.ErrEof) || errors.Is(err, astiav.ErrEagain) {
err = nil
break
}
err = fmt.Errorf("main: getting frame failed: %w", err)
return
}
// Reset picture type
s.filterFrame.SetPictureType(astiav.PictureTypeNone)
// Encode and write frame
if err = encodeWriteFrame(s.filterFrame, s); err != nil {
err = fmt.Errorf("main: encoding and writing frame failed: %w", err)
return
}
}
return
}
func encodeWriteFrame(f *astiav.Frame, s *stream) (err error) {
// Unref packet
s.encPkt.Unref()
// Send frame
if err = s.encCodecContext.SendFrame(f); err != nil {
err = fmt.Errorf("main: sending frame failed: %w", err)
return
}
// Loop
for {
// Receive packet
if err = s.encCodecContext.ReceivePacket(s.encPkt); err != nil {
if errors.Is(err, astiav.ErrEof) || errors.Is(err, astiav.ErrEagain) {
err = nil
break
}
err = fmt.Errorf("main: receiving packet failed: %w", err)
return
}
// Update pkt
s.encPkt.SetStreamIndex(s.outputStream.Index())
s.encPkt.RescaleTs(s.encCodecContext.TimeBase(), s.outputStream.TimeBase())
// Write frame
if err = outputFormatContext.WriteInterleavedFrame(s.encPkt); err != nil {
err = fmt.Errorf("main: writing frame failed: %w", err)
return
}
}
return
}

View File

@@ -1,32 +0,0 @@
package astiav
//#cgo pkg-config: libavfilter
//#include <libavfilter/avfilter.h>
import "C"
import "unsafe"
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavfilter/avfilter.h#L165
type Filter struct {
c *C.struct_AVFilter
}
func newFilterFromC(c *C.struct_AVFilter) *Filter {
if c == nil {
return nil
}
return &Filter{c: c}
}
func FindFilterByName(n string) *Filter {
cn := C.CString(n)
defer C.free(unsafe.Pointer(cn))
return newFilterFromC(C.avfilter_get_by_name(cn))
}
func (f *Filter) Name() string {
return C.GoString(f.c.name)
}
func (f *Filter) String() string {
return f.Name()
}

View File

@@ -1,13 +0,0 @@
package astiav
//#cgo pkg-config: libavfilter
//#include <libavfilter/avfilter.h>
import "C"
type FilterCommandFlag int64
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavfilter/avfilter.h#L739
const (
FilterCommandFlagOne = FilterCommandFlag(C.AVFILTER_CMD_FLAG_ONE)
FilterCommandFlagFast = FilterCommandFlag(C.AVFILTER_CMD_FLAG_FAST)
)

View File

@@ -1,77 +0,0 @@
package astiav
//#cgo pkg-config: libavfilter libavutil
//#include <libavfilter/avfilter.h>
//#include <libavfilter/buffersink.h>
//#include <libavfilter/buffersrc.h>
//#include <libavutil/frame.h>
import "C"
import (
"math"
"unsafe"
)
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavfilter/avfilter.h#L67
type FilterContext struct {
c *C.struct_AVFilterContext
}
func newFilterContext(c *C.struct_AVFilterContext) *FilterContext {
if c == nil {
return nil
}
fc := &FilterContext{c: c}
classers.set(fc)
return fc
}
var _ Classer = (*FilterContext)(nil)
func (fc *FilterContext) Free() {
classers.del(fc)
C.avfilter_free(fc.c)
}
func (fc *FilterContext) BuffersrcAddFrame(f *Frame, fs BuffersrcFlags) error {
var cf *C.struct_AVFrame
if f != nil {
cf = f.c
}
return newError(C.av_buffersrc_add_frame_flags(fc.c, cf, C.int(fs)))
}
func (fc *FilterContext) BuffersinkGetFrame(f *Frame, fs BuffersinkFlags) error {
var cf *C.struct_AVFrame
if f != nil {
cf = f.c
}
return newError(C.av_buffersink_get_frame_flags(fc.c, cf, C.int(fs)))
}
func (fc *FilterContext) Class() *Class {
return newClassFromC(unsafe.Pointer(fc.c))
}
func (fc *FilterContext) NbInputs() int {
return int(fc.c.nb_inputs)
}
func (fc *FilterContext) NbOutputs() int {
return int(fc.c.nb_outputs)
}
func (fc *FilterContext) Inputs() (ls []*FilterLink) {
lcs := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.struct_AVFilterLink)(nil))](*C.struct_AVFilterLink))(unsafe.Pointer(fc.c.inputs))
for i := 0; i < fc.NbInputs(); i++ {
ls = append(ls, newFilterLinkFromC(lcs[i]))
}
return
}
func (fc *FilterContext) Outputs() (ls []*FilterLink) {
lcs := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.struct_AVFilterLink)(nil))](*C.struct_AVFilterLink))(unsafe.Pointer(fc.c.outputs))
for i := 0; i < fc.NbOutputs(); i++ {
ls = append(ls, newFilterLinkFromC(lcs[i]))
}
return
}

View File

@@ -1,100 +0,0 @@
package astiav
//#cgo pkg-config: libavfilter
//#include <libavfilter/avfilter.h>
import "C"
import (
"strings"
"unsafe"
)
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavfilter/avfilter.h#L861
type FilterGraph struct {
c *C.struct_AVFilterGraph
}
func newFilterGraphFromC(c *C.struct_AVFilterGraph) *FilterGraph {
if c == nil {
return nil
}
g := &FilterGraph{c: c}
classers.set(g)
return g
}
var _ Classer = (*FilterGraph)(nil)
func AllocFilterGraph() *FilterGraph {
return newFilterGraphFromC(C.avfilter_graph_alloc())
}
func (g *FilterGraph) Free() {
classers.del(g)
if g.c != nil {
C.avfilter_graph_free(&g.c)
}
}
func (g *FilterGraph) String() string {
return C.GoString(C.avfilter_graph_dump(g.c, nil))
}
func (g *FilterGraph) Class() *Class {
return newClassFromC(unsafe.Pointer(g.c))
}
type FilterArgs map[string]string
func (args FilterArgs) String() string {
var ss []string
for k, v := range args {
ss = append(ss, k+"="+v)
}
return strings.Join(ss, ":")
}
func (g *FilterGraph) NewFilterContext(f *Filter, name string, args FilterArgs) (*FilterContext, error) {
ca := (*C.char)(nil)
if len(args) > 0 {
ca = C.CString(args.String())
defer C.free(unsafe.Pointer(ca))
}
cn := C.CString(name)
defer C.free(unsafe.Pointer(cn))
var c *C.struct_AVFilterContext
if err := newError(C.avfilter_graph_create_filter(&c, f.c, cn, ca, nil, g.c)); err != nil {
return nil, err
}
return newFilterContext(c), nil
}
func (g *FilterGraph) Parse(content string, inputs, outputs *FilterInOut) error {
cc := C.CString(content)
defer C.free(unsafe.Pointer(cc))
var ic **C.struct_AVFilterInOut
if inputs != nil {
ic = &inputs.c
}
var oc **C.struct_AVFilterInOut
if outputs != nil {
oc = &outputs.c
}
return newError(C.avfilter_graph_parse_ptr(g.c, cc, ic, oc, nil))
}
func (g *FilterGraph) Configure() error {
return newError(C.avfilter_graph_config(g.c, nil))
}
func (g *FilterGraph) SendCommand(target, cmd, args string, f FilterCommandFlags) (response string, err error) {
targetc := C.CString(target)
defer C.free(unsafe.Pointer(targetc))
cmdc := C.CString(cmd)
defer C.free(unsafe.Pointer(cmdc))
argsc := C.CString(args)
defer C.free(unsafe.Pointer(argsc))
response, err = stringFromC(255, func(buf *C.char, size C.size_t) error {
return newError(C.avfilter_graph_send_command(g.c, targetc, cmdc, argsc, buf, C.int(size), C.int(f)))
})
return
}

View File

@@ -1,96 +0,0 @@
// TODO Fix https://github.com/asticode/go-astiav/actions/runs/5853322732/job/15867145888
//go:build !windows
package astiav
import (
"fmt"
"strconv"
"testing"
"github.com/stretchr/testify/require"
)
func TestFilterGraph(t *testing.T) {
fg := AllocFilterGraph()
defer fg.Free()
cl := fg.Class()
require.NotNil(t, cl)
require.Equal(t, "AVFilterGraph", cl.Name())
bufferSink := FindFilterByName("buffersink")
require.NotNil(t, bufferSink)
fcOut, err := fg.NewFilterContext(bufferSink, "filter_out", nil)
require.NoError(t, err)
defer fcOut.Free()
cl = fcOut.Class()
require.NotNil(t, cl)
require.Equal(t, "AVFilter", cl.Name())
inputs := AllocFilterInOut()
defer inputs.Free()
inputs.SetName("out")
inputs.SetFilterContext(fcOut)
inputs.SetPadIdx(0)
inputs.SetNext(nil)
var outputs *FilterInOut
defer func() {
if outputs != nil {
outputs.Free()
}
}()
var fcIns []*FilterContext
for i := 0; i < 2; i++ {
bufferSrc := FindFilterByName("buffer")
require.NotNil(t, bufferSrc)
fcIn, err := fg.NewFilterContext(bufferSrc, fmt.Sprintf("filter_in_%d", i+1), FilterArgs{
"pix_fmt": strconv.Itoa(int(PixelFormatYuv420P)),
"pixel_aspect": "1/1",
"time_base": "1/1000",
"video_size": "1x1",
})
require.NoError(t, err)
fcIns = append(fcIns, fcIn)
defer fcIn.Free()
o := AllocFilterInOut()
o.SetName(fmt.Sprintf("input_%d", i+1))
o.SetFilterContext(fcIn)
o.SetPadIdx(0)
o.SetNext(outputs)
outputs = o
}
err = fg.Parse("[input_1]scale=2x2[scaled_1];[input_2]scale=3x3[scaled_2];[scaled_1][scaled_2]overlay", inputs, outputs)
require.NoError(t, err)
err = fg.Configure()
require.NoError(t, err)
require.Equal(t, 1, fcOut.NbInputs())
require.Equal(t, 1, len(fcOut.Inputs()))
require.Equal(t, NewRational(1, 1000), fcOut.Inputs()[0].TimeBase())
require.Equal(t, 0, fcOut.NbOutputs())
for _, fc := range fcIns {
require.Equal(t, 0, fc.NbInputs())
require.Equal(t, 1, fc.NbOutputs())
require.Equal(t, 1, len(fc.Outputs()))
require.Equal(t, NewRational(1, 1000), fc.Outputs()[0].TimeBase())
}
resp, err := fg.SendCommand("scale", "invalid", "a", NewFilterCommandFlags())
require.Error(t, err)
require.Empty(t, resp)
resp, err = fg.SendCommand("scale", "width", "4", NewFilterCommandFlags().Add(FilterCommandFlagOne))
require.NoError(t, err)
require.Empty(t, resp)
require.Equal(t, " +--------------+\nParsed_overlay_2:default--[2x2 1:1 yuv420p]--default| filter_out |\n | (buffersink) |\n +--------------+\n\n+-------------+\n| filter_in_1 |default--[1x1 1:1 yuv420p]--Parsed_scale_0:default\n| (buffer) |\n+-------------+\n\n+-------------+\n| filter_in_2 |default--[1x1 1:1 yuv420p]--Parsed_scale_1:default\n| (buffer) |\n+-------------+\n\n +----------------+\nfilter_in_1:default--[1x1 1:1 yuv420p]--default| Parsed_scale_0 |default--[4x2 1:2 yuv420p]--Parsed_overlay_2:main\n | (scale) |\n +----------------+\n\n +----------------+\nfilter_in_2:default--[1x1 1:1 yuv420p]--default| Parsed_scale_1 |default--[3x3 1:1 yuva420p]--Parsed_overlay_2:overlay\n | (scale) |\n +----------------+\n\n +------------------+\nParsed_scale_0:default--[4x2 1:2 yuv420p]------main| Parsed_overlay_2 |default--[2x2 1:1 yuv420p]--filter_out:default\nParsed_scale_1:default--[3x3 1:1 yuva420p]--overlay| (overlay) |\n +------------------+\n\n", fg.String())
// TODO Test BuffersrcAddFrame
// TODO Test BuffersinkGetFrame
}

View File

@@ -1,45 +0,0 @@
package astiav
//#cgo pkg-config: libavfilter
//#include <libavfilter/avfilter.h>
import "C"
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavfilter/avfilter.h#L1021
type FilterInOut struct {
c *C.struct_AVFilterInOut
}
func newFilterInOutFromC(c *C.struct_AVFilterInOut) *FilterInOut {
if c == nil {
return nil
}
return &FilterInOut{c: c}
}
func AllocFilterInOut() *FilterInOut {
return newFilterInOutFromC(C.avfilter_inout_alloc())
}
func (i *FilterInOut) Free() {
C.avfilter_inout_free(&i.c)
}
func (i *FilterInOut) SetName(n string) {
i.c.name = C.CString(n)
}
func (i *FilterInOut) SetFilterContext(c *FilterContext) {
i.c.filter_ctx = (*C.struct_AVFilterContext)(c.c)
}
func (i *FilterInOut) SetPadIdx(idx int) {
i.c.pad_idx = C.int(idx)
}
func (i *FilterInOut) SetNext(n *FilterInOut) {
var nc *C.struct_AVFilterInOut
if n != nil {
nc = n.c
}
i.c.next = nc
}

View File

@@ -1,21 +0,0 @@
package astiav
//#cgo pkg-config: libavfilter
//#include <libavfilter/avfilter.h>
import "C"
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavfilter/avfilter.h#L471
type FilterLink struct {
c *C.struct_AVFilterLink
}
func newFilterLinkFromC(c *C.struct_AVFilterLink) *FilterLink {
if c == nil {
return nil
}
return &FilterLink{c: c}
}
func (l *FilterLink) TimeBase() Rational {
return newRationalFromC(l.c.time_base)
}

View File

@@ -1,14 +0,0 @@
package astiav
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestFilter(t *testing.T) {
f := FindFilterByName("format")
require.NotNil(t, f)
require.Equal(t, "format", f.Name())
require.Equal(t, "format", f.String())
}

View File

@@ -1,326 +0,0 @@
// Code generated by astiav. DO NOT EDIT.
package astiav
import (
"github.com/asticode/go-astikit"
)
type BuffersinkFlags astikit.BitFlags
func NewBuffersinkFlags(fs ...BuffersinkFlag) BuffersinkFlags {
o := BuffersinkFlags(0)
for _, f := range fs {
o = o.Add(f)
}
return o
}
func (fs BuffersinkFlags) Add(f BuffersinkFlag) BuffersinkFlags {
return BuffersinkFlags(astikit.BitFlags(fs).Add(uint64(f)))
}
func (fs BuffersinkFlags) Del(f BuffersinkFlag) BuffersinkFlags {
return BuffersinkFlags(astikit.BitFlags(fs).Del(uint64(f)))
}
func (fs BuffersinkFlags) Has(f BuffersinkFlag) bool { return astikit.BitFlags(fs).Has(uint64(f)) }
type BuffersrcFlags astikit.BitFlags
func NewBuffersrcFlags(fs ...BuffersrcFlag) BuffersrcFlags {
o := BuffersrcFlags(0)
for _, f := range fs {
o = o.Add(f)
}
return o
}
func (fs BuffersrcFlags) Add(f BuffersrcFlag) BuffersrcFlags {
return BuffersrcFlags(astikit.BitFlags(fs).Add(uint64(f)))
}
func (fs BuffersrcFlags) Del(f BuffersrcFlag) BuffersrcFlags {
return BuffersrcFlags(astikit.BitFlags(fs).Del(uint64(f)))
}
func (fs BuffersrcFlags) Has(f BuffersrcFlag) bool { return astikit.BitFlags(fs).Has(uint64(f)) }
type CodecContextFlags astikit.BitFlags
func NewCodecContextFlags(fs ...CodecContextFlag) CodecContextFlags {
o := CodecContextFlags(0)
for _, f := range fs {
o = o.Add(f)
}
return o
}
func (fs CodecContextFlags) Add(f CodecContextFlag) CodecContextFlags {
return CodecContextFlags(astikit.BitFlags(fs).Add(uint64(f)))
}
func (fs CodecContextFlags) Del(f CodecContextFlag) CodecContextFlags {
return CodecContextFlags(astikit.BitFlags(fs).Del(uint64(f)))
}
func (fs CodecContextFlags) Has(f CodecContextFlag) bool { return astikit.BitFlags(fs).Has(uint64(f)) }
type CodecContextFlags2 astikit.BitFlags
func NewCodecContextFlags2(fs ...CodecContextFlag2) CodecContextFlags2 {
o := CodecContextFlags2(0)
for _, f := range fs {
o = o.Add(f)
}
return o
}
func (fs CodecContextFlags2) Add(f CodecContextFlag2) CodecContextFlags2 {
return CodecContextFlags2(astikit.BitFlags(fs).Add(uint64(f)))
}
func (fs CodecContextFlags2) Del(f CodecContextFlag2) CodecContextFlags2 {
return CodecContextFlags2(astikit.BitFlags(fs).Del(uint64(f)))
}
func (fs CodecContextFlags2) Has(f CodecContextFlag2) bool { return astikit.BitFlags(fs).Has(uint64(f)) }
type CodecHardwareConfigMethodFlags astikit.BitFlags
func NewCodecHardwareConfigMethodFlags(fs ...CodecHardwareConfigMethodFlag) CodecHardwareConfigMethodFlags {
o := CodecHardwareConfigMethodFlags(0)
for _, f := range fs {
o = o.Add(f)
}
return o
}
func (fs CodecHardwareConfigMethodFlags) Add(f CodecHardwareConfigMethodFlag) CodecHardwareConfigMethodFlags {
return CodecHardwareConfigMethodFlags(astikit.BitFlags(fs).Add(uint64(f)))
}
func (fs CodecHardwareConfigMethodFlags) Del(f CodecHardwareConfigMethodFlag) CodecHardwareConfigMethodFlags {
return CodecHardwareConfigMethodFlags(astikit.BitFlags(fs).Del(uint64(f)))
}
func (fs CodecHardwareConfigMethodFlags) Has(f CodecHardwareConfigMethodFlag) bool { return astikit.BitFlags(fs).Has(uint64(f)) }
type DictionaryFlags astikit.BitFlags
func NewDictionaryFlags(fs ...DictionaryFlag) DictionaryFlags {
o := DictionaryFlags(0)
for _, f := range fs {
o = o.Add(f)
}
return o
}
func (fs DictionaryFlags) Add(f DictionaryFlag) DictionaryFlags {
return DictionaryFlags(astikit.BitFlags(fs).Add(uint64(f)))
}
func (fs DictionaryFlags) Del(f DictionaryFlag) DictionaryFlags {
return DictionaryFlags(astikit.BitFlags(fs).Del(uint64(f)))
}
func (fs DictionaryFlags) Has(f DictionaryFlag) bool { return astikit.BitFlags(fs).Has(uint64(f)) }
type FilterCommandFlags astikit.BitFlags
func NewFilterCommandFlags(fs ...FilterCommandFlag) FilterCommandFlags {
o := FilterCommandFlags(0)
for _, f := range fs {
o = o.Add(f)
}
return o
}
func (fs FilterCommandFlags) Add(f FilterCommandFlag) FilterCommandFlags {
return FilterCommandFlags(astikit.BitFlags(fs).Add(uint64(f)))
}
func (fs FilterCommandFlags) Del(f FilterCommandFlag) FilterCommandFlags {
return FilterCommandFlags(astikit.BitFlags(fs).Del(uint64(f)))
}
func (fs FilterCommandFlags) Has(f FilterCommandFlag) bool { return astikit.BitFlags(fs).Has(uint64(f)) }
type FormatContextFlags astikit.BitFlags
func NewFormatContextFlags(fs ...FormatContextFlag) FormatContextFlags {
o := FormatContextFlags(0)
for _, f := range fs {
o = o.Add(f)
}
return o
}
func (fs FormatContextFlags) Add(f FormatContextFlag) FormatContextFlags {
return FormatContextFlags(astikit.BitFlags(fs).Add(uint64(f)))
}
func (fs FormatContextFlags) Del(f FormatContextFlag) FormatContextFlags {
return FormatContextFlags(astikit.BitFlags(fs).Del(uint64(f)))
}
func (fs FormatContextFlags) Has(f FormatContextFlag) bool { return astikit.BitFlags(fs).Has(uint64(f)) }
type FormatContextCtxFlags astikit.BitFlags
func NewFormatContextCtxFlags(fs ...FormatContextCtxFlag) FormatContextCtxFlags {
o := FormatContextCtxFlags(0)
for _, f := range fs {
o = o.Add(f)
}
return o
}
func (fs FormatContextCtxFlags) Add(f FormatContextCtxFlag) FormatContextCtxFlags {
return FormatContextCtxFlags(astikit.BitFlags(fs).Add(uint64(f)))
}
func (fs FormatContextCtxFlags) Del(f FormatContextCtxFlag) FormatContextCtxFlags {
return FormatContextCtxFlags(astikit.BitFlags(fs).Del(uint64(f)))
}
func (fs FormatContextCtxFlags) Has(f FormatContextCtxFlag) bool { return astikit.BitFlags(fs).Has(uint64(f)) }
type FormatEventFlags astikit.BitFlags
func NewFormatEventFlags(fs ...FormatEventFlag) FormatEventFlags {
o := FormatEventFlags(0)
for _, f := range fs {
o = o.Add(f)
}
return o
}
func (fs FormatEventFlags) Add(f FormatEventFlag) FormatEventFlags {
return FormatEventFlags(astikit.BitFlags(fs).Add(uint64(f)))
}
func (fs FormatEventFlags) Del(f FormatEventFlag) FormatEventFlags {
return FormatEventFlags(astikit.BitFlags(fs).Del(uint64(f)))
}
func (fs FormatEventFlags) Has(f FormatEventFlag) bool { return astikit.BitFlags(fs).Has(uint64(f)) }
type IOContextFlags astikit.BitFlags
func NewIOContextFlags(fs ...IOContextFlag) IOContextFlags {
o := IOContextFlags(0)
for _, f := range fs {
o = o.Add(f)
}
return o
}
func (fs IOContextFlags) Add(f IOContextFlag) IOContextFlags {
return IOContextFlags(astikit.BitFlags(fs).Add(uint64(f)))
}
func (fs IOContextFlags) Del(f IOContextFlag) IOContextFlags {
return IOContextFlags(astikit.BitFlags(fs).Del(uint64(f)))
}
func (fs IOContextFlags) Has(f IOContextFlag) bool { return astikit.BitFlags(fs).Has(uint64(f)) }
type IOFormatFlags astikit.BitFlags
func NewIOFormatFlags(fs ...IOFormatFlag) IOFormatFlags {
o := IOFormatFlags(0)
for _, f := range fs {
o = o.Add(f)
}
return o
}
func (fs IOFormatFlags) Add(f IOFormatFlag) IOFormatFlags {
return IOFormatFlags(astikit.BitFlags(fs).Add(uint64(f)))
}
func (fs IOFormatFlags) Del(f IOFormatFlag) IOFormatFlags {
return IOFormatFlags(astikit.BitFlags(fs).Del(uint64(f)))
}
func (fs IOFormatFlags) Has(f IOFormatFlag) bool { return astikit.BitFlags(fs).Has(uint64(f)) }
type PacketFlags astikit.BitFlags
func NewPacketFlags(fs ...PacketFlag) PacketFlags {
o := PacketFlags(0)
for _, f := range fs {
o = o.Add(f)
}
return o
}
func (fs PacketFlags) Add(f PacketFlag) PacketFlags {
return PacketFlags(astikit.BitFlags(fs).Add(uint64(f)))
}
func (fs PacketFlags) Del(f PacketFlag) PacketFlags {
return PacketFlags(astikit.BitFlags(fs).Del(uint64(f)))
}
func (fs PacketFlags) Has(f PacketFlag) bool { return astikit.BitFlags(fs).Has(uint64(f)) }
type SeekFlags astikit.BitFlags
func NewSeekFlags(fs ...SeekFlag) SeekFlags {
o := SeekFlags(0)
for _, f := range fs {
o = o.Add(f)
}
return o
}
func (fs SeekFlags) Add(f SeekFlag) SeekFlags {
return SeekFlags(astikit.BitFlags(fs).Add(uint64(f)))
}
func (fs SeekFlags) Del(f SeekFlag) SeekFlags {
return SeekFlags(astikit.BitFlags(fs).Del(uint64(f)))
}
func (fs SeekFlags) Has(f SeekFlag) bool { return astikit.BitFlags(fs).Has(uint64(f)) }
type SoftwareScaleContextFlags astikit.BitFlags
func NewSoftwareScaleContextFlags(fs ...SoftwareScaleContextFlag) SoftwareScaleContextFlags {
o := SoftwareScaleContextFlags(0)
for _, f := range fs {
o = o.Add(f)
}
return o
}
func (fs SoftwareScaleContextFlags) Add(f SoftwareScaleContextFlag) SoftwareScaleContextFlags {
return SoftwareScaleContextFlags(astikit.BitFlags(fs).Add(uint64(f)))
}
func (fs SoftwareScaleContextFlags) Del(f SoftwareScaleContextFlag) SoftwareScaleContextFlags {
return SoftwareScaleContextFlags(astikit.BitFlags(fs).Del(uint64(f)))
}
func (fs SoftwareScaleContextFlags) Has(f SoftwareScaleContextFlag) bool { return astikit.BitFlags(fs).Has(uint64(f)) }
type StreamEventFlags astikit.BitFlags
func NewStreamEventFlags(fs ...StreamEventFlag) StreamEventFlags {
o := StreamEventFlags(0)
for _, f := range fs {
o = o.Add(f)
}
return o
}
func (fs StreamEventFlags) Add(f StreamEventFlag) StreamEventFlags {
return StreamEventFlags(astikit.BitFlags(fs).Add(uint64(f)))
}
func (fs StreamEventFlags) Del(f StreamEventFlag) StreamEventFlags {
return StreamEventFlags(astikit.BitFlags(fs).Del(uint64(f)))
}
func (fs StreamEventFlags) Has(f StreamEventFlag) bool { return astikit.BitFlags(fs).Has(uint64(f)) }

View File

@@ -1,151 +0,0 @@
// Code generated by astiav. DO NOT EDIT.
package astiav
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestBuffersinkFlags(t *testing.T) {
fs := NewBuffersinkFlags(BuffersinkFlag(1))
require.True(t, fs.Has(BuffersinkFlag(1)))
fs = fs.Add(BuffersinkFlag(2))
require.True(t, fs.Has(BuffersinkFlag(2)))
fs = fs.Del(BuffersinkFlag(2))
require.False(t, fs.Has(BuffersinkFlag(2)))
}
func TestBuffersrcFlags(t *testing.T) {
fs := NewBuffersrcFlags(BuffersrcFlag(1))
require.True(t, fs.Has(BuffersrcFlag(1)))
fs = fs.Add(BuffersrcFlag(2))
require.True(t, fs.Has(BuffersrcFlag(2)))
fs = fs.Del(BuffersrcFlag(2))
require.False(t, fs.Has(BuffersrcFlag(2)))
}
func TestCodecContextFlags(t *testing.T) {
fs := NewCodecContextFlags(CodecContextFlag(1))
require.True(t, fs.Has(CodecContextFlag(1)))
fs = fs.Add(CodecContextFlag(2))
require.True(t, fs.Has(CodecContextFlag(2)))
fs = fs.Del(CodecContextFlag(2))
require.False(t, fs.Has(CodecContextFlag(2)))
}
func TestCodecContextFlags2(t *testing.T) {
fs := NewCodecContextFlags2(CodecContextFlag2(1))
require.True(t, fs.Has(CodecContextFlag2(1)))
fs = fs.Add(CodecContextFlag2(2))
require.True(t, fs.Has(CodecContextFlag2(2)))
fs = fs.Del(CodecContextFlag2(2))
require.False(t, fs.Has(CodecContextFlag2(2)))
}
func TestCodecHardwareConfigMethodFlags(t *testing.T) {
fs := NewCodecHardwareConfigMethodFlags(CodecHardwareConfigMethodFlag(1))
require.True(t, fs.Has(CodecHardwareConfigMethodFlag(1)))
fs = fs.Add(CodecHardwareConfigMethodFlag(2))
require.True(t, fs.Has(CodecHardwareConfigMethodFlag(2)))
fs = fs.Del(CodecHardwareConfigMethodFlag(2))
require.False(t, fs.Has(CodecHardwareConfigMethodFlag(2)))
}
func TestDictionaryFlags(t *testing.T) {
fs := NewDictionaryFlags(DictionaryFlag(1))
require.True(t, fs.Has(DictionaryFlag(1)))
fs = fs.Add(DictionaryFlag(2))
require.True(t, fs.Has(DictionaryFlag(2)))
fs = fs.Del(DictionaryFlag(2))
require.False(t, fs.Has(DictionaryFlag(2)))
}
func TestFilterCommandFlags(t *testing.T) {
fs := NewFilterCommandFlags(FilterCommandFlag(1))
require.True(t, fs.Has(FilterCommandFlag(1)))
fs = fs.Add(FilterCommandFlag(2))
require.True(t, fs.Has(FilterCommandFlag(2)))
fs = fs.Del(FilterCommandFlag(2))
require.False(t, fs.Has(FilterCommandFlag(2)))
}
func TestFormatContextFlags(t *testing.T) {
fs := NewFormatContextFlags(FormatContextFlag(1))
require.True(t, fs.Has(FormatContextFlag(1)))
fs = fs.Add(FormatContextFlag(2))
require.True(t, fs.Has(FormatContextFlag(2)))
fs = fs.Del(FormatContextFlag(2))
require.False(t, fs.Has(FormatContextFlag(2)))
}
func TestFormatContextCtxFlags(t *testing.T) {
fs := NewFormatContextCtxFlags(FormatContextCtxFlag(1))
require.True(t, fs.Has(FormatContextCtxFlag(1)))
fs = fs.Add(FormatContextCtxFlag(2))
require.True(t, fs.Has(FormatContextCtxFlag(2)))
fs = fs.Del(FormatContextCtxFlag(2))
require.False(t, fs.Has(FormatContextCtxFlag(2)))
}
func TestFormatEventFlags(t *testing.T) {
fs := NewFormatEventFlags(FormatEventFlag(1))
require.True(t, fs.Has(FormatEventFlag(1)))
fs = fs.Add(FormatEventFlag(2))
require.True(t, fs.Has(FormatEventFlag(2)))
fs = fs.Del(FormatEventFlag(2))
require.False(t, fs.Has(FormatEventFlag(2)))
}
func TestIOContextFlags(t *testing.T) {
fs := NewIOContextFlags(IOContextFlag(1))
require.True(t, fs.Has(IOContextFlag(1)))
fs = fs.Add(IOContextFlag(2))
require.True(t, fs.Has(IOContextFlag(2)))
fs = fs.Del(IOContextFlag(2))
require.False(t, fs.Has(IOContextFlag(2)))
}
func TestIOFormatFlags(t *testing.T) {
fs := NewIOFormatFlags(IOFormatFlag(1))
require.True(t, fs.Has(IOFormatFlag(1)))
fs = fs.Add(IOFormatFlag(2))
require.True(t, fs.Has(IOFormatFlag(2)))
fs = fs.Del(IOFormatFlag(2))
require.False(t, fs.Has(IOFormatFlag(2)))
}
func TestPacketFlags(t *testing.T) {
fs := NewPacketFlags(PacketFlag(1))
require.True(t, fs.Has(PacketFlag(1)))
fs = fs.Add(PacketFlag(2))
require.True(t, fs.Has(PacketFlag(2)))
fs = fs.Del(PacketFlag(2))
require.False(t, fs.Has(PacketFlag(2)))
}
func TestSeekFlags(t *testing.T) {
fs := NewSeekFlags(SeekFlag(1))
require.True(t, fs.Has(SeekFlag(1)))
fs = fs.Add(SeekFlag(2))
require.True(t, fs.Has(SeekFlag(2)))
fs = fs.Del(SeekFlag(2))
require.False(t, fs.Has(SeekFlag(2)))
}
func TestSoftwareScaleContextFlags(t *testing.T) {
fs := NewSoftwareScaleContextFlags(SoftwareScaleContextFlag(1))
require.True(t, fs.Has(SoftwareScaleContextFlag(1)))
fs = fs.Add(SoftwareScaleContextFlag(2))
require.True(t, fs.Has(SoftwareScaleContextFlag(2)))
fs = fs.Del(SoftwareScaleContextFlag(2))
require.False(t, fs.Has(SoftwareScaleContextFlag(2)))
}
func TestStreamEventFlags(t *testing.T) {
fs := NewStreamEventFlags(StreamEventFlag(1))
require.True(t, fs.Has(StreamEventFlag(1)))
fs = fs.Add(StreamEventFlag(2))
require.True(t, fs.Has(StreamEventFlag(2)))
fs = fs.Del(StreamEventFlag(2))
require.False(t, fs.Has(StreamEventFlag(2)))
}

View File

@@ -1,270 +0,0 @@
package astiav
//#cgo pkg-config: libavcodec libavformat
//#include <libavcodec/avcodec.h>
//#include <libavformat/avformat.h>
import "C"
import (
"math"
"unsafe"
)
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavformat/avformat.h#L1202
type FormatContext struct {
c *C.struct_AVFormatContext
}
func newFormatContextFromC(c *C.struct_AVFormatContext) *FormatContext {
if c == nil {
return nil
}
fc := &FormatContext{c: c}
classers.set(fc)
return fc
}
var _ Classer = (*FormatContext)(nil)
func AllocFormatContext() *FormatContext {
return newFormatContextFromC(C.avformat_alloc_context())
}
func AllocOutputFormatContext(of *OutputFormat, formatName, filename string) (*FormatContext, error) {
fonc := (*C.char)(nil)
if len(formatName) > 0 {
fonc = C.CString(formatName)
defer C.free(unsafe.Pointer(fonc))
}
finc := (*C.char)(nil)
if len(filename) > 0 {
finc = C.CString(filename)
defer C.free(unsafe.Pointer(finc))
}
var ofc *C.struct_AVOutputFormat
if of != nil {
ofc = of.c
}
var fcc *C.struct_AVFormatContext
if err := newError(C.avformat_alloc_output_context2(&fcc, ofc, fonc, finc)); err != nil {
return nil, err
}
return newFormatContextFromC(fcc), nil
}
func (fc *FormatContext) Free() {
classers.del(fc)
C.avformat_free_context(fc.c)
}
func (fc *FormatContext) BitRate() int64 {
return int64(fc.c.bit_rate)
}
func (fc *FormatContext) Class() *Class {
return newClassFromC(unsafe.Pointer(fc.c))
}
func (fc *FormatContext) CtxFlags() FormatContextCtxFlags {
return FormatContextCtxFlags(fc.c.ctx_flags)
}
func (fc *FormatContext) Duration() int64 {
return int64(fc.c.duration)
}
func (fc *FormatContext) EventFlags() FormatEventFlags {
return FormatEventFlags(fc.c.event_flags)
}
func (fc *FormatContext) Flags() FormatContextFlags {
return FormatContextFlags(fc.c.flags)
}
func (fc *FormatContext) SetFlags(f FormatContextFlags) {
fc.c.flags = C.int(f)
}
func (fc *FormatContext) SetInterruptCallback() IOInterrupter {
i := newDefaultIOInterrupter()
fc.c.interrupt_callback = i.c
return i
}
func (fc *FormatContext) InputFormat() *InputFormat {
return newInputFormatFromC(fc.c.iformat)
}
func (fc *FormatContext) IOFlags() IOContextFlags {
return IOContextFlags(fc.c.avio_flags)
}
func (fc *FormatContext) MaxAnalyzeDuration() int64 {
return int64(fc.c.max_analyze_duration)
}
func (fc *FormatContext) Metadata() *Dictionary {
return newDictionaryFromC(fc.c.metadata)
}
func (fc *FormatContext) NbStreams() int {
return int(fc.c.nb_streams)
}
func (fc *FormatContext) OutputFormat() *OutputFormat {
return newOutputFormatFromC(fc.c.oformat)
}
func (fc *FormatContext) Pb() *IOContext {
// If the io context has been created using the format context's OpenInput() method, we need to
// make sure to return the same go struct as the one stored in classers
if c, ok := classers.get(unsafe.Pointer(fc.c.pb)); ok {
if v, ok := c.(*IOContext); ok {
return v
}
}
return newIOContextFromC(fc.c.pb)
}
func (fc *FormatContext) SetPb(i *IOContext) {
fc.c.pb = i.c
}
func (fc *FormatContext) StartTime() int64 {
return int64(fc.c.start_time)
}
func (fc *FormatContext) Streams() (ss []*Stream) {
scs := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.struct_AVStream)(nil))](*C.struct_AVStream))(unsafe.Pointer(fc.c.streams))
for i := 0; i < fc.NbStreams(); i++ {
ss = append(ss, newStreamFromC(scs[i]))
}
return
}
func (fc *FormatContext) StrictStdCompliance() StrictStdCompliance {
return StrictStdCompliance(fc.c.strict_std_compliance)
}
func (fc *FormatContext) SetStrictStdCompliance(strictStdCompliance StrictStdCompliance) {
fc.c.strict_std_compliance = C.int(strictStdCompliance)
}
func (fc *FormatContext) OpenInput(url string, fmt *InputFormat, d *Dictionary) error {
urlc := C.CString(url)
defer C.free(unsafe.Pointer(urlc))
var dc **C.struct_AVDictionary
if d != nil {
dc = &d.c
}
var fmtc *C.struct_AVInputFormat
if fmt != nil {
fmtc = fmt.c
}
if err := newError(C.avformat_open_input(&fc.c, urlc, fmtc, dc)); err != nil {
return err
}
if pb := fc.Pb(); pb != nil {
classers.set(pb)
}
return nil
}
func (fc *FormatContext) CloseInput() {
if pb := fc.Pb(); pb != nil {
classers.del(pb)
}
classers.del(fc)
if fc.c != nil {
C.avformat_close_input(&fc.c)
}
}
func (fc *FormatContext) NewStream(c *Codec) *Stream {
var cc *C.struct_AVCodec
if c != nil {
cc = c.c
}
return newStreamFromC(C.avformat_new_stream(fc.c, cc))
}
func (fc *FormatContext) FindStreamInfo(d *Dictionary) error {
var dc **C.struct_AVDictionary
if d != nil {
dc = &d.c
}
return newError(C.avformat_find_stream_info(fc.c, dc))
}
func (fc *FormatContext) ReadFrame(p *Packet) error {
var pc *C.struct_AVPacket
if p != nil {
pc = p.c
}
return newError(C.av_read_frame(fc.c, pc))
}
func (fc *FormatContext) SeekFrame(streamIndex int, timestamp int64, f SeekFlags) error {
return newError(C.av_seek_frame(fc.c, C.int(streamIndex), C.int64_t(timestamp), C.int(f)))
}
func (fc *FormatContext) Flush() error {
return newError(C.avformat_flush(fc.c))
}
func (fc *FormatContext) WriteHeader(d *Dictionary) error {
var dc **C.struct_AVDictionary
if d != nil {
dc = &d.c
}
return newError(C.avformat_write_header(fc.c, dc))
}
func (fc *FormatContext) WriteFrame(p *Packet) error {
var pc *C.struct_AVPacket
if p != nil {
pc = p.c
}
return newError(C.av_write_frame(fc.c, pc))
}
func (fc *FormatContext) WriteInterleavedFrame(p *Packet) error {
var pc *C.struct_AVPacket
if p != nil {
pc = p.c
}
return newError(C.av_interleaved_write_frame(fc.c, pc))
}
func (fc *FormatContext) WriteTrailer() error {
return newError(C.av_write_trailer(fc.c))
}
func (fc *FormatContext) GuessSampleAspectRatio(s *Stream, f *Frame) Rational {
var cf *C.struct_AVFrame
if f != nil {
cf = f.c
}
return newRationalFromC(C.av_guess_sample_aspect_ratio(fc.c, s.c, cf))
}
func (fc *FormatContext) GuessFrameRate(s *Stream, f *Frame) Rational {
var cf *C.struct_AVFrame
if f != nil {
cf = f.c
}
return newRationalFromC(C.av_guess_frame_rate(fc.c, s.c, cf))
}
func (fc *FormatContext) SDPCreate() (string, error) {
return sdpCreate([]*FormatContext{fc})
}
func sdpCreate(fcs []*FormatContext) (string, error) {
return stringFromC(1024, func(buf *C.char, size C.size_t) error {
fccs := []*C.struct_AVFormatContext{}
for _, fc := range fcs {
fccs = append(fccs, fc.c)
}
return newError(C.av_sdp_create(&fccs[0], C.int(len(fcs)), buf, C.int(size)))
})
}

View File

@@ -1,13 +0,0 @@
package astiav
//#cgo pkg-config: libavformat
//#include <libavformat/avformat.h>
import "C"
type FormatContextCtxFlag int64
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavformat/avformat.h#L1153
const (
FormatContextCtxFlagNoHeader = FormatContextCtxFlag(C.AVFMTCTX_NOHEADER)
FormatContextCtxFlagUnseekable = FormatContextCtxFlag(C.AVFMTCTX_UNSEEKABLE)
)

View File

@@ -1,27 +0,0 @@
package astiav
//#cgo pkg-config: libavformat
//#include <libavformat/avformat.h>
import "C"
type FormatContextFlag int64
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavformat/avformat.h#L1321
const (
FormatContextFlagGenPts = FormatContextFlag(C.AVFMT_FLAG_GENPTS)
FormatContextFlagIgnidx = FormatContextFlag(C.AVFMT_FLAG_IGNIDX)
FormatContextFlagNonblock = FormatContextFlag(C.AVFMT_FLAG_NONBLOCK)
FormatContextFlagIgnDts = FormatContextFlag(C.AVFMT_FLAG_IGNDTS)
FormatContextFlagNofillin = FormatContextFlag(C.AVFMT_FLAG_NOFILLIN)
FormatContextFlagNoparse = FormatContextFlag(C.AVFMT_FLAG_NOPARSE)
FormatContextFlagNobuffer = FormatContextFlag(C.AVFMT_FLAG_NOBUFFER)
FormatContextFlagCustomIo = FormatContextFlag(C.AVFMT_FLAG_CUSTOM_IO)
FormatContextFlagDiscardCorrupt = FormatContextFlag(C.AVFMT_FLAG_DISCARD_CORRUPT)
FormatContextFlagFlushPackets = FormatContextFlag(C.AVFMT_FLAG_FLUSH_PACKETS)
FormatContextFlagBitexact = FormatContextFlag(C.AVFMT_FLAG_BITEXACT)
FormatContextFlagSortDts = FormatContextFlag(C.AVFMT_FLAG_SORT_DTS)
FormatContextFlagPrivOpt = FormatContextFlag(C.AVFMT_FLAG_PRIV_OPT)
FormatContextFlagFastSeek = FormatContextFlag(C.AVFMT_FLAG_FAST_SEEK)
FormatContextFlagShortest = FormatContextFlag(C.AVFMT_FLAG_SHORTEST)
FormatContextFlagAutoBsf = FormatContextFlag(C.AVFMT_FLAG_AUTO_BSF)
)

View File

@@ -1,74 +0,0 @@
package astiav
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestFormatContext(t *testing.T) {
fc1, err := globalHelper.inputFormatContext("video.mp4")
require.NoError(t, err)
ss := fc1.Streams()
require.Len(t, ss, 2)
s1 := ss[0]
require.Equal(t, int64(607583), fc1.BitRate())
require.Equal(t, NewFormatContextCtxFlags(0), fc1.CtxFlags())
require.Equal(t, int64(5014000), fc1.Duration())
require.True(t, fc1.EventFlags().Has(FormatEventFlagMetadataUpdated))
require.True(t, fc1.Flags().Has(FormatContextFlagAutoBsf))
require.Equal(t, NewRational(24, 1), fc1.GuessFrameRate(s1, nil))
require.Equal(t, NewRational(1, 1), fc1.GuessSampleAspectRatio(s1, nil))
require.True(t, fc1.InputFormat().Flags().Has(IOFormatFlagNoByteSeek))
require.Equal(t, IOContextFlags(0), fc1.IOFlags())
require.Equal(t, int64(0), fc1.MaxAnalyzeDuration())
require.Equal(t, "isom", fc1.Metadata().Get("major_brand", nil, NewDictionaryFlags()).Value())
require.Equal(t, int64(0), fc1.StartTime())
require.Equal(t, 2, fc1.NbStreams())
require.Len(t, fc1.Streams(), 2)
cl := fc1.Class()
require.NotNil(t, cl)
require.Equal(t, "AVFormatContext", cl.Name())
sdp, err := fc1.SDPCreate()
require.NoError(t, err)
require.Equal(t, "v=0\r\no=- 0 0 IN IP4 127.0.0.1\r\ns=Big Buck Bunny\r\nt=0 0\r\na=tool:libavformat LIBAVFORMAT_VERSION\r\nm=video 0 RTP/AVP 96\r\nb=AS:441\r\na=rtpmap:96 H264/90000\r\na=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z0LADasgKDPz4CIAAAMAAgAAAwBhHihUkA==,aM48gA==; profile-level-id=42C00D\r\na=control:streamid=0\r\nm=audio 0 RTP/AVP 97\r\nb=AS:161\r\na=rtpmap:97 MPEG4-GENERIC/48000/2\r\na=fmtp:97 profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3; config=1190\r\na=control:streamid=1\r\n", sdp)
fc2, err := AllocOutputFormatContext(nil, "", "/tmp/test.mp4")
require.NoError(t, err)
defer fc2.Free()
require.True(t, fc2.OutputFormat().Flags().Has(IOFormatFlagGlobalheader))
fc3 := AllocFormatContext()
require.NotNil(t, fc3)
defer fc3.Free()
c, err := OpenIOContext("testdata/video.mp4", NewIOContextFlags(IOContextFlagRead))
require.NoError(t, err)
defer c.Close() //nolint:errcheck
fc3.SetPb(c)
fc3.SetStrictStdCompliance(StrictStdComplianceExperimental)
fc3.SetFlags(NewFormatContextFlags(FormatContextFlagAutoBsf))
require.NotNil(t, fc3.Pb())
require.Equal(t, StrictStdComplianceExperimental, fc3.StrictStdCompliance())
require.True(t, fc3.Flags().Has(FormatContextFlagAutoBsf))
s2 := fc3.NewStream(nil)
require.NotNil(t, s2)
s3 := fc3.NewStream(nil)
require.NotNil(t, s3)
require.Equal(t, 1, s3.Index())
fc4 := AllocFormatContext()
require.NotNil(t, fc4)
defer fc4.Free()
fc4.SetInterruptCallback().Interrupt()
require.ErrorIs(t, fc4.OpenInput("testdata/video.mp4", nil, nil), ErrExit)
// TODO Test ReadFrame
// TODO Test SeekFrame
// TODO Test Flush
// TODO Test WriteHeader
// TODO Test WriteFrame
// TODO Test WriteInterleavedFrame
// TODO Test WriteTrailer
}

View File

@@ -1,12 +0,0 @@
package astiav
//#cgo pkg-config: libavformat
//#include <libavformat/avformat.h>
import "C"
type FormatEventFlag int64
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavformat/avformat.h#L1519
const (
FormatEventFlagMetadataUpdated = FormatEventFlag(C.AVFMT_EVENT_FLAG_METADATA_UPDATED)
)

View File

@@ -1,219 +0,0 @@
package astiav
//#cgo pkg-config: libavutil
//#include <libavutil/channel_layout.h>
//#include <libavutil/frame.h>
//#include <libavutil/imgutils.h>
//#include <libavutil/samplefmt.h>
//#include <libavutil/hwcontext.h>
import "C"
import (
"unsafe"
)
const NumDataPointers = uint(C.AV_NUM_DATA_POINTERS)
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavutil/frame.h#L317
type Frame struct {
c *C.struct_AVFrame
}
func newFrameFromC(c *C.struct_AVFrame) *Frame {
if c == nil {
return nil
}
return &Frame{c: c}
}
func AllocFrame() *Frame {
return newFrameFromC(C.av_frame_alloc())
}
func (f *Frame) AllocBuffer(align int) error {
return newError(C.av_frame_get_buffer(f.c, C.int(align)))
}
func (f *Frame) AllocImage(align int) error {
return newError(C.av_image_alloc(&f.c.data[0], &f.c.linesize[0], f.c.width, f.c.height, (C.enum_AVPixelFormat)(f.c.format), C.int(align)))
}
func (f *Frame) AllocSamples(align int) error {
return newError(C.av_samples_alloc(&f.c.data[0], &f.c.linesize[0], f.c.ch_layout.nb_channels, f.c.nb_samples, (C.enum_AVSampleFormat)(f.c.format), C.int(align)))
}
func (f *Frame) ChannelLayout() ChannelLayout {
l, _ := newChannelLayoutFromC(&f.c.ch_layout).clone()
return l
}
func (f *Frame) SetChannelLayout(l ChannelLayout) {
l.copy(&f.c.ch_layout) //nolint: errcheck
}
func (f *Frame) ColorRange() ColorRange {
return ColorRange(f.c.color_range)
}
func (f *Frame) SetColorRange(r ColorRange) {
f.c.color_range = C.enum_AVColorRange(r)
}
func (f *Frame) Data() *FrameData {
return newFrameData(newFrameDataFrame(f))
}
func (f *Frame) Height() int {
return int(f.c.height)
}
func (f *Frame) SetHeight(h int) {
f.c.height = C.int(h)
}
func (f *Frame) KeyFrame() bool {
return int(f.c.key_frame) > 0
}
func (f *Frame) SetKeyFrame(k bool) {
i := 0
if k {
i = 1
}
f.c.key_frame = C.int(i)
}
func (f *Frame) ImageBufferSize(align int) (int, error) {
ret := C.av_image_get_buffer_size((C.enum_AVSampleFormat)(f.c.format), f.c.width, f.c.height, C.int(align))
if ret < 0 {
return 0, newError(ret)
}
return int(ret), nil
}
func (f *Frame) ImageCopyToBuffer(b []byte, align int) (int, error) {
ret := C.av_image_copy_to_buffer((*C.uint8_t)(unsafe.Pointer(&b[0])), C.int(len(b)), &f.c.data[0], &f.c.linesize[0], (C.enum_AVSampleFormat)(f.c.format), f.c.width, f.c.height, C.int(align))
if ret < 0 {
return 0, newError(ret)
}
return int(ret), nil
}
func (f *Frame) ImageFillBlack() error {
linesize := [NumDataPointers]C.ptrdiff_t{}
for i := 0; i < int(NumDataPointers); i++ {
linesize[i] = C.ptrdiff_t(f.c.linesize[i])
}
return newError(C.av_image_fill_black(&f.c.data[0], &linesize[0], (C.enum_AVPixelFormat)(f.c.format), (C.enum_AVColorRange)(f.c.color_range), f.c.width, f.c.height))
}
func (f *Frame) Linesize() [NumDataPointers]int {
o := [NumDataPointers]int{}
for i := 0; i < int(NumDataPointers); i++ {
o[i] = int(f.c.linesize[i])
}
return o
}
func (f *Frame) NbSamples() int {
return int(f.c.nb_samples)
}
func (f *Frame) SetNbSamples(n int) {
f.c.nb_samples = C.int(n)
}
func (f *Frame) PictureType() PictureType {
return PictureType(f.c.pict_type)
}
func (f *Frame) SetPictureType(t PictureType) {
f.c.pict_type = C.enum_AVPictureType(t)
}
func (f *Frame) PixelFormat() PixelFormat {
return PixelFormat(f.c.format)
}
func (f *Frame) SetPixelFormat(pf PixelFormat) {
f.c.format = C.int(pf)
}
func (f *Frame) PktDts() int64 {
return int64(f.c.pkt_dts)
}
func (f *Frame) Pts() int64 {
return int64(f.c.pts)
}
func (f *Frame) SetPts(i int64) {
f.c.pts = C.int64_t(i)
}
func (f *Frame) SampleAspectRatio() Rational {
return newRationalFromC(f.c.sample_aspect_ratio)
}
func (f *Frame) SetSampleAspectRatio(r Rational) {
f.c.sample_aspect_ratio = r.c
}
func (f *Frame) SampleFormat() SampleFormat {
return SampleFormat(f.c.format)
}
func (f *Frame) SetSampleFormat(sf SampleFormat) {
f.c.format = C.int(sf)
}
func (f *Frame) SampleRate() int {
return int(f.c.sample_rate)
}
func (f *Frame) SetSampleRate(r int) {
f.c.sample_rate = C.int(r)
}
func (f *Frame) NewSideData(t FrameSideDataType, size uint64) *FrameSideData {
return newFrameSideDataFromC(C.av_frame_new_side_data(f.c, (C.enum_AVFrameSideDataType)(t), C.size_t(size)))
}
func (f *Frame) SideData(t FrameSideDataType) *FrameSideData {
return newFrameSideDataFromC(C.av_frame_get_side_data(f.c, (C.enum_AVFrameSideDataType)(t)))
}
func (f *Frame) Width() int {
return int(f.c.width)
}
func (f *Frame) SetWidth(w int) {
f.c.width = C.int(w)
}
func (f *Frame) TransferHardwareData(dst *Frame) error {
return newError(C.av_hwframe_transfer_data(dst.c, f.c, 0))
}
func (f *Frame) Free() {
C.av_frame_free(&f.c)
}
func (f *Frame) Ref(src *Frame) error {
return newError(C.av_frame_ref(f.c, src.c))
}
func (f *Frame) Clone() *Frame {
return newFrameFromC(C.av_frame_clone(f.c))
}
func (f *Frame) Unref() {
C.av_frame_unref(f.c)
}
func (f *Frame) MoveRef(src *Frame) {
C.av_frame_move_ref(f.c, src.c)
}
func (f *Frame) UnsafePointer() unsafe.Pointer {
return unsafe.Pointer(f.c)
}

View File

@@ -1,207 +0,0 @@
package astiav
//#include <stdint.h>
import "C"
import (
"errors"
"fmt"
"image"
"strings"
)
type FrameData struct {
f frameDataFramer
}
type frameDataFramer interface {
height() int
imageBufferSize(align int) (int, error)
imageCopyToBuffer(b []byte, align int) (int, error)
linesize(i int) int
pixelFormat() PixelFormat
planeBytes(i int) []byte
width() int
}
func newFrameData(f frameDataFramer) *FrameData {
return &FrameData{f: f}
}
func (d *FrameData) Bytes(align int) ([]byte, error) {
switch {
// Video
case d.f.height() > 0 && d.f.width() > 0:
// Get buffer size
s, err := d.f.imageBufferSize(align)
if err != nil {
return nil, fmt.Errorf("astiav: getting image buffer size failed: %w", err)
}
// Invalid buffer size
if s == 0 {
return nil, errors.New("astiav: invalid image buffer size")
}
// Create buffer
b := make([]byte, s)
// Copy image to buffer
if _, err = d.f.imageCopyToBuffer(b, align); err != nil {
return nil, fmt.Errorf("astiav: copying image to buffer failed: %w", err)
}
return b, nil
}
return nil, errors.New("astiav: frame type not implemented")
}
// Always returns non-premultiplied formats when dealing with alpha channels, however this might not
// always be accurate. In this case, use your own format in .ToImage()
func (d *FrameData) GuessImageFormat() (image.Image, error) {
switch d.f.pixelFormat() {
case PixelFormatGray8:
return &image.Gray{}, nil
case PixelFormatGray16Be:
return &image.Gray16{}, nil
case PixelFormatRgb0, PixelFormat0Rgb, PixelFormatRgb4, PixelFormatRgb8:
return &image.RGBA{}, nil
case PixelFormatRgba:
return &image.NRGBA{}, nil
case PixelFormatRgba64Be:
return &image.NRGBA64{}, nil
case PixelFormatYuva420P,
PixelFormatYuva422P,
PixelFormatYuva444P:
return &image.NYCbCrA{}, nil
case PixelFormatYuv410P,
PixelFormatYuv411P, PixelFormatYuvj411P,
PixelFormatYuv420P, PixelFormatYuvj420P,
PixelFormatYuv422P, PixelFormatYuvj422P,
PixelFormatYuv440P, PixelFormatYuvj440P,
PixelFormatYuv444P, PixelFormatYuvj444P:
return &image.YCbCr{}, nil
}
return nil, fmt.Errorf("astiav: pixel format %s not handled by Go", d.f.pixelFormat())
}
func (d *FrameData) imageYCbCrSubsampleRatio() image.YCbCrSubsampleRatio {
name := d.f.pixelFormat().Name()
for s, r := range map[string]image.YCbCrSubsampleRatio{
"410": image.YCbCrSubsampleRatio410,
"411": image.YCbCrSubsampleRatio411,
"420": image.YCbCrSubsampleRatio420,
"422": image.YCbCrSubsampleRatio422,
"440": image.YCbCrSubsampleRatio440,
"444": image.YCbCrSubsampleRatio444,
} {
if strings.Contains(name, s) {
return r
}
}
return image.YCbCrSubsampleRatio444
}
func (d *FrameData) toImagePix(pix *[]uint8, stride *int, rect *image.Rectangle) {
*pix = d.f.planeBytes(0)
if v := d.f.linesize(0); *stride != v {
*stride = v
}
if w, h := d.f.width(), d.f.height(); rect.Dy() != w || rect.Dx() != h {
*rect = image.Rect(0, 0, w, h)
}
}
func (d *FrameData) toImageYCbCr(y, cb, cr *[]uint8, yStride, cStride *int, subsampleRatio *image.YCbCrSubsampleRatio, rect *image.Rectangle) {
*y = d.f.planeBytes(0)
*cb = d.f.planeBytes(1)
*cr = d.f.planeBytes(2)
if v := d.f.linesize(0); *yStride != v {
*yStride = v
}
if v := d.f.linesize(1); *cStride != v {
*cStride = v
}
if v := d.imageYCbCrSubsampleRatio(); *subsampleRatio != v {
*subsampleRatio = v
}
if w, h := d.f.width(), d.f.height(); rect.Dy() != w || rect.Dx() != h {
*rect = image.Rect(0, 0, w, h)
}
}
func (d *FrameData) toImageYCbCrA(y, cb, cr, a *[]uint8, yStride, cStride, aStride *int, subsampleRatio *image.YCbCrSubsampleRatio, rect *image.Rectangle) {
d.toImageYCbCr(y, cb, cr, yStride, cStride, subsampleRatio, rect)
*a = d.f.planeBytes(3)
if v := d.f.linesize(3); *aStride != v {
*aStride = v
}
}
func (d *FrameData) ToImage(dst image.Image) error {
if v, ok := dst.(*image.Alpha); ok {
d.toImagePix(&v.Pix, &v.Stride, &v.Rect)
} else if v, ok := dst.(*image.Alpha16); ok {
d.toImagePix(&v.Pix, &v.Stride, &v.Rect)
} else if v, ok := dst.(*image.CMYK); ok {
d.toImagePix(&v.Pix, &v.Stride, &v.Rect)
} else if v, ok := dst.(*image.Gray); ok {
d.toImagePix(&v.Pix, &v.Stride, &v.Rect)
} else if v, ok := dst.(*image.Gray16); ok {
d.toImagePix(&v.Pix, &v.Stride, &v.Rect)
} else if v, ok := dst.(*image.NRGBA); ok {
d.toImagePix(&v.Pix, &v.Stride, &v.Rect)
} else if v, ok := dst.(*image.NRGBA64); ok {
d.toImagePix(&v.Pix, &v.Stride, &v.Rect)
} else if v, ok := dst.(*image.NYCbCrA); ok {
d.toImageYCbCrA(&v.Y, &v.Cb, &v.Cr, &v.A, &v.YStride, &v.CStride, &v.AStride, &v.SubsampleRatio, &v.Rect)
} else if v, ok := dst.(*image.RGBA); ok {
d.toImagePix(&v.Pix, &v.Stride, &v.Rect)
} else if v, ok := dst.(*image.RGBA64); ok {
d.toImagePix(&v.Pix, &v.Stride, &v.Rect)
} else if v, ok := dst.(*image.YCbCr); ok {
d.toImageYCbCr(&v.Y, &v.Cb, &v.Cr, &v.YStride, &v.CStride, &v.SubsampleRatio, &v.Rect)
} else {
return errors.New("astiav: image format is not handled")
}
return nil
}
var _ frameDataFramer = (*frameDataFrame)(nil)
type frameDataFrame struct {
f *Frame
}
func newFrameDataFrame(f *Frame) *frameDataFrame {
return &frameDataFrame{f: f}
}
func (f *frameDataFrame) height() int {
return f.f.Height()
}
func (f *frameDataFrame) imageBufferSize(align int) (int, error) {
return f.f.ImageBufferSize(align)
}
func (f *frameDataFrame) imageCopyToBuffer(b []byte, align int) (int, error) {
return f.f.ImageCopyToBuffer(b, align)
}
func (f *frameDataFrame) linesize(i int) int {
return f.f.Linesize()[i]
}
func (f *frameDataFrame) pixelFormat() PixelFormat {
return f.f.PixelFormat()
}
func (f *frameDataFrame) planeBytes(i int) []byte {
return bytesFromC(func(size *C.size_t) *C.uint8_t {
*size = C.size_t(int(f.f.c.linesize[i]) * f.f.Height())
return f.f.c.data[i]
})
}
func (f *frameDataFrame) width() int {
return f.f.Width()
}

View File

@@ -1,316 +0,0 @@
package astiav
import (
"image"
"image/png"
"os"
"testing"
"github.com/stretchr/testify/require"
)
type mockedFrameDataFrame struct {
h int
imageBytes []byte
linesizes []int
pf PixelFormat
planesBytes [][]byte
w int
}
var _ frameDataFramer = (*mockedFrameDataFrame)(nil)
func (f *mockedFrameDataFrame) height() int {
return f.h
}
func (f *mockedFrameDataFrame) imageBufferSize(align int) (int, error) {
return len(f.imageBytes), nil
}
func (f *mockedFrameDataFrame) imageCopyToBuffer(b []byte, align int) (int, error) {
copy(b, f.imageBytes)
return len(f.imageBytes), nil
}
func (f *mockedFrameDataFrame) linesize(i int) int {
return f.linesizes[i]
}
func (f *mockedFrameDataFrame) pixelFormat() PixelFormat {
return f.pf
}
func (f *mockedFrameDataFrame) planeBytes(i int) []byte {
return f.planesBytes[i]
}
func (f *mockedFrameDataFrame) width() int {
return f.w
}
func TestFrameDataInternal(t *testing.T) {
fdf := &mockedFrameDataFrame{}
fd := newFrameData(fdf)
for _, v := range []struct {
err bool
i image.Image
pfs []PixelFormat
}{
{
i: &image.Gray{},
pfs: []PixelFormat{PixelFormatGray8},
},
{
i: &image.Gray16{},
pfs: []PixelFormat{PixelFormatGray16Be},
},
{
i: &image.RGBA{},
pfs: []PixelFormat{
PixelFormatRgb0,
PixelFormat0Rgb,
PixelFormatRgb4,
PixelFormatRgb8,
},
},
{
i: &image.NRGBA{},
pfs: []PixelFormat{PixelFormatRgba},
},
{
i: &image.NRGBA64{},
pfs: []PixelFormat{PixelFormatRgba64Be},
},
{
i: &image.NYCbCrA{},
pfs: []PixelFormat{
PixelFormatYuva420P,
PixelFormatYuva422P,
PixelFormatYuva444P,
},
},
{
i: &image.YCbCr{},
pfs: []PixelFormat{
PixelFormatYuv410P,
PixelFormatYuv411P,
PixelFormatYuvj411P,
PixelFormatYuv420P,
PixelFormatYuvj420P,
PixelFormatYuv422P,
PixelFormatYuvj422P,
PixelFormatYuv440P,
PixelFormatYuvj440P,
PixelFormatYuv444P,
PixelFormatYuvj444P,
},
},
{
err: true,
pfs: []PixelFormat{PixelFormatAbgr},
},
} {
for _, pf := range v.pfs {
fdf.pf = pf
i, err := fd.GuessImageFormat()
if v.err {
require.Error(t, err)
} else {
require.IsType(t, v.i, i)
}
}
}
fdf.imageBytes = []byte{0, 1, 2, 3}
_, err := fd.Bytes(0)
require.Error(t, err)
fdf.h = 1
fdf.w = 2
b, err := fd.Bytes(0)
require.NoError(t, err)
require.Equal(t, fdf.imageBytes, b)
for _, v := range []struct {
e image.Image
err bool
i image.Image
linesizes []int
pixelFormat PixelFormat
planesBytes [][]byte
}{
{
e: &image.Alpha{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
Rect: image.Rect(0, 0, 2, 1),
},
i: &image.Alpha{},
linesizes: []int{1},
pixelFormat: PixelFormatRgba,
planesBytes: [][]byte{{0, 1, 2, 3}},
},
{
e: &image.Alpha16{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
Rect: image.Rect(0, 0, 2, 1),
},
i: &image.Alpha16{},
linesizes: []int{1},
pixelFormat: PixelFormatRgba,
planesBytes: [][]byte{{0, 1, 2, 3}},
},
{
e: &image.CMYK{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
Rect: image.Rect(0, 0, 2, 1),
},
i: &image.CMYK{},
linesizes: []int{1},
pixelFormat: PixelFormatRgba,
planesBytes: [][]byte{{0, 1, 2, 3}},
},
{
e: &image.Gray{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
Rect: image.Rect(0, 0, 2, 1),
},
i: &image.Gray{},
linesizes: []int{1},
pixelFormat: PixelFormatRgba,
planesBytes: [][]byte{{0, 1, 2, 3}},
},
{
e: &image.Gray16{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
Rect: image.Rect(0, 0, 2, 1),
},
i: &image.Gray16{},
linesizes: []int{1},
pixelFormat: PixelFormatRgba,
planesBytes: [][]byte{{0, 1, 2, 3}},
},
{
e: &image.NRGBA{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
Rect: image.Rect(0, 0, 2, 1),
},
i: &image.NRGBA{},
linesizes: []int{1},
pixelFormat: PixelFormatRgba,
planesBytes: [][]byte{{0, 1, 2, 3}},
},
{
e: &image.NRGBA64{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
Rect: image.Rect(0, 0, 2, 1),
},
i: &image.NRGBA64{},
linesizes: []int{1},
pixelFormat: PixelFormatRgba,
planesBytes: [][]byte{{0, 1, 2, 3}},
},
{
e: &image.NYCbCrA{
A: []byte{6, 7},
AStride: 4,
YCbCr: image.YCbCr{
Y: []byte{0, 1},
Cb: []byte{2, 3},
Cr: []byte{4, 5},
YStride: 1,
CStride: 2,
SubsampleRatio: image.YCbCrSubsampleRatio444,
Rect: image.Rect(0, 0, 2, 1),
},
},
i: &image.NYCbCrA{},
linesizes: []int{1, 2, 3, 4},
pixelFormat: PixelFormatYuv444P,
planesBytes: [][]byte{{0, 1}, {2, 3}, {4, 5}, {6, 7}},
},
{
e: &image.RGBA{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
Rect: image.Rect(0, 0, 2, 1),
},
i: &image.RGBA{},
linesizes: []int{1},
pixelFormat: PixelFormatRgba,
planesBytes: [][]byte{{0, 1, 2, 3}},
},
{
e: &image.RGBA64{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
Rect: image.Rect(0, 0, 2, 1),
},
i: &image.RGBA64{},
linesizes: []int{1},
pixelFormat: PixelFormatRgba,
planesBytes: [][]byte{{0, 1, 2, 3}},
},
{
e: &image.YCbCr{
Y: []byte{0, 1},
Cb: []byte{2, 3},
Cr: []byte{4, 5},
YStride: 1,
CStride: 2,
SubsampleRatio: image.YCbCrSubsampleRatio420,
Rect: image.Rect(0, 0, 2, 1),
},
i: &image.YCbCr{},
linesizes: []int{1, 2, 3},
pixelFormat: PixelFormatYuv420P,
planesBytes: [][]byte{{0, 1}, {2, 3}, {4, 5}},
},
} {
fdf.linesizes = v.linesizes
fdf.pf = v.pixelFormat
fdf.planesBytes = v.planesBytes
err = fd.ToImage(v.i)
if v.err {
require.Error(t, err)
} else {
require.Equal(t, v.e, v.i)
}
}
}
func TestFrameData(t *testing.T) {
const (
name = "image-rgba"
ext = "png"
)
f, err := globalHelper.inputLastFrame(name+"."+ext, MediaTypeVideo)
require.NoError(t, err)
fd := f.Data()
b1, err := fd.Bytes(1)
require.NoError(t, err)
b2, err := os.ReadFile("testdata/" + name + "-bytes")
require.NoError(t, err)
require.Equal(t, b1, b2)
f1, err := os.Open("testdata/" + name + "." + ext)
require.NoError(t, err)
defer f1.Close()
i1, err := fd.GuessImageFormat()
require.NoError(t, err)
require.NoError(t, err)
require.NoError(t, fd.ToImage(i1))
i2, err := png.Decode(f1)
require.NoError(t, err)
require.Equal(t, i1, i2)
}

View File

@@ -1,36 +0,0 @@
package astiav
//#cgo pkg-config: libavutil
//#include <libavutil/frame.h>
import "C"
import (
"math"
"unsafe"
)
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavutil/frame.h#L223
type FrameSideData struct {
c *C.struct_AVFrameSideData
}
func newFrameSideDataFromC(c *C.struct_AVFrameSideData) *FrameSideData {
if c == nil {
return nil
}
return &FrameSideData{c: c}
}
func (d *FrameSideData) Data() []byte {
return bytesFromC(func(size *C.size_t) *C.uint8_t {
*size = d.c.size
return d.c.data
})
}
func (d *FrameSideData) SetData(b []byte) {
C.memcpy(unsafe.Pointer(d.c.data), unsafe.Pointer(&b[0]), C.size_t(math.Min(float64(len(b)), float64(d.c.size))))
}
func (d *FrameSideData) Type() FrameSideDataType {
return FrameSideDataType(d.c._type)
}

View File

@@ -1,33 +0,0 @@
package astiav
//#cgo pkg-config: libavutil
//#include <libavutil/frame.h>
import "C"
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavutil/frame.h#L48
type FrameSideDataType C.enum_AVFrameSideDataType
const (
FrameSideDataTypePanscan = FrameSideDataType(C.AV_FRAME_DATA_PANSCAN)
FrameSideDataTypeA53Cc = FrameSideDataType(C.AV_FRAME_DATA_A53_CC)
FrameSideDataTypeStereo3D = FrameSideDataType(C.AV_FRAME_DATA_STEREO3D)
FrameSideDataTypeMatrixencoding = FrameSideDataType(C.AV_FRAME_DATA_MATRIXENCODING)
FrameSideDataTypeDownmixInfo = FrameSideDataType(C.AV_FRAME_DATA_DOWNMIX_INFO)
FrameSideDataTypeReplaygain = FrameSideDataType(C.AV_FRAME_DATA_REPLAYGAIN)
FrameSideDataTypeDisplaymatrix = FrameSideDataType(C.AV_FRAME_DATA_DISPLAYMATRIX)
FrameSideDataTypeAfd = FrameSideDataType(C.AV_FRAME_DATA_AFD)
FrameSideDataTypeMotionVectors = FrameSideDataType(C.AV_FRAME_DATA_MOTION_VECTORS)
FrameSideDataTypeSkipSamples = FrameSideDataType(C.AV_FRAME_DATA_SKIP_SAMPLES)
FrameSideDataTypeAudioServiceType = FrameSideDataType(C.AV_FRAME_DATA_AUDIO_SERVICE_TYPE)
FrameSideDataTypeMasteringDisplayMetadata = FrameSideDataType(C.AV_FRAME_DATA_MASTERING_DISPLAY_METADATA)
FrameSideDataTypeGopTimecode = FrameSideDataType(C.AV_FRAME_DATA_GOP_TIMECODE)
FrameSideDataTypeSpherical = FrameSideDataType(C.AV_FRAME_DATA_SPHERICAL)
FrameSideDataTypeContentLightLevel = FrameSideDataType(C.AV_FRAME_DATA_CONTENT_LIGHT_LEVEL)
FrameSideDataTypeIccProfile = FrameSideDataType(C.AV_FRAME_DATA_ICC_PROFILE)
FrameSideDataTypeS12MTimecode = FrameSideDataType(C.AV_FRAME_DATA_S12M_TIMECODE)
FrameSideDataTypeDynamicHdrPlus = FrameSideDataType(C.AV_FRAME_DATA_DYNAMIC_HDR_PLUS)
FrameSideDataTypeRegionsOfInterest = FrameSideDataType(C.AV_FRAME_DATA_REGIONS_OF_INTEREST)
FrameSideDataTypeVideoEncParams = FrameSideDataType(C.AV_FRAME_DATA_VIDEO_ENC_PARAMS)
FrameSideDataTypeSeiUnregistered = FrameSideDataType(C.AV_FRAME_DATA_SEI_UNREGISTERED)
FrameSideDataTypeFilmGrainParams = FrameSideDataType(C.AV_FRAME_DATA_FILM_GRAIN_PARAMS)
)

View File

@@ -1,116 +0,0 @@
package astiav
import (
"bytes"
"testing"
"unsafe"
"github.com/stretchr/testify/require"
)
func TestFrame(t *testing.T) {
f1, err := globalHelper.inputLastFrame("video.mp4", MediaTypeVideo)
require.NoError(t, err)
// Should be "{384, 192, 192, 0, 0, 0, 0, 0}" but for some reason it"s "{320, 160, 160, 0, 0, 0, 0, 0}"
// on darwin when testing using github
require.Contains(t, [][8]int{
{384, 192, 192, 0, 0, 0, 0, 0},
{320, 160, 160, 0, 0, 0, 0, 0},
}, f1.Linesize())
require.Equal(t, int64(60928), f1.PktDts())
require.Equal(t, unsafe.Pointer(f1.c), f1.UnsafePointer())
f2 := AllocFrame()
require.NotNil(t, f2)
defer f2.Free()
f2.SetChannelLayout(ChannelLayout21)
f2.SetColorRange(ColorRangeJpeg)
f2.SetHeight(2)
f2.SetKeyFrame(true)
f2.SetNbSamples(4)
f2.SetPictureType(PictureTypeB)
f2.SetPixelFormat(PixelFormat0Bgr)
require.Equal(t, PixelFormat0Bgr, f2.PixelFormat()) // Need to test it right away as sample format actually updates the same field
f2.SetPts(7)
f2.SetSampleAspectRatio(NewRational(10, 2))
f2.SetSampleFormat(SampleFormatDbl)
require.Equal(t, SampleFormatDbl, f2.SampleFormat())
f2.SetSampleRate(9)
f2.SetWidth(10)
require.True(t, f2.ChannelLayout().Equal(ChannelLayout21))
require.Equal(t, ColorRangeJpeg, f2.ColorRange())
require.Equal(t, 2, f2.Height())
require.True(t, f2.KeyFrame())
require.Equal(t, 4, f2.NbSamples())
require.Equal(t, PictureTypeB, f2.PictureType())
require.Equal(t, int64(7), f2.Pts())
require.Equal(t, NewRational(10, 2), f2.SampleAspectRatio())
require.Equal(t, 9, f2.SampleRate())
require.Equal(t, 10, f2.Width())
f3 := f1.Clone()
require.NotNil(t, f3)
defer f3.Free()
require.Equal(t, 180, f3.Height())
err = f2.AllocBuffer(0)
require.NoError(t, err)
err = f3.Ref(f2)
require.NoError(t, err)
require.Equal(t, 2, f3.Height())
f3.MoveRef(f1)
require.Equal(t, 180, f3.Height())
require.Equal(t, 0, f1.Height())
f3.Unref()
require.Equal(t, 0, f3.Height())
f4 := AllocFrame()
require.NotNil(t, f4)
defer f4.Free()
f4.SetNbSamples(960)
f4.SetChannelLayout(ChannelLayoutStereo)
f4.SetSampleFormat(SampleFormatS16)
f4.SetSampleRate(48000)
err = f4.AllocBuffer(0)
require.NoError(t, err)
err = f4.AllocSamples(0)
require.NoError(t, err)
f5 := AllocFrame()
require.NotNil(t, f5)
defer f5.Free()
sd := f5.NewSideData(FrameSideDataTypeAudioServiceType, 4)
require.NotNil(t, sd)
sd.SetData([]byte{1, 2, 3})
sd = f5.SideData(FrameSideDataTypeAudioServiceType)
require.NotNil(t, sd)
require.Equal(t, FrameSideDataTypeAudioServiceType, sd.Type())
require.True(t, bytes.HasPrefix(sd.Data(), []byte{1, 2, 3}))
require.Len(t, sd.Data(), 4)
sd.SetData([]byte{1, 2, 3, 4, 5})
sd = f5.SideData(FrameSideDataTypeAudioServiceType)
require.NotNil(t, sd)
require.Equal(t, []byte{1, 2, 3, 4}, sd.Data())
f6 := AllocFrame()
require.NotNil(t, f6)
defer f6.Free()
f6.SetColorRange(ColorRangeUnspecified)
f6.SetHeight(2)
f6.SetPixelFormat(PixelFormatYuv420P)
f6.SetWidth(4)
const align = 1
require.NoError(t, f6.AllocBuffer(align))
require.NoError(t, f6.AllocImage(align))
require.NoError(t, f6.ImageFillBlack())
n, err := f6.ImageBufferSize(align)
require.NoError(t, err)
require.Equal(t, 12, n)
b := make([]byte, n)
n, err = f6.ImageCopyToBuffer(b, align)
require.NoError(t, err)
require.Equal(t, 12, n)
require.Equal(t, []byte{0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x80, 0x80, 0x80, 0x80}, b)
}

View File

@@ -1,16 +0,0 @@
module github.com/asticode/go-astiav
go 1.17
require (
github.com/asticode/go-astikit v0.42.0
github.com/stretchr/testify v1.7.0
)
require (
github.com/davecgh/go-spew v1.1.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
)
//replace github.com/asticode/go-astikit => ../go-astikit

View File

@@ -1,13 +0,0 @@
github.com/asticode/go-astikit v0.42.0 h1:pnir/2KLUSr0527Tv908iAH6EGYYrYta132vvjXsH5w=
github.com/asticode/go-astikit v0.42.0/go.mod h1:h4ly7idim1tNhaVkdVBeXQZEE3L0xblP7fCWbgwipF0=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -1,31 +0,0 @@
package astiav
//#cgo pkg-config: libavutil libavcodec
//#include <libavcodec/avcodec.h>
//#include <libavutil/hwcontext.h>
import "C"
import (
"unsafe"
)
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavutil/hwcontext.h#L61
type HardwareDeviceContext struct {
c *C.AVBufferRef
}
func CreateHardwareDeviceContext(t HardwareDeviceType, device string, options *Dictionary) (*HardwareDeviceContext, error) {
hdc := HardwareDeviceContext{}
deviceC := (*C.char)(nil)
if device != "" {
deviceC = C.CString(device)
defer C.free(unsafe.Pointer(deviceC))
}
optionsC := (*C.struct_AVDictionary)(nil)
if options != nil {
optionsC = options.c
}
if err := newError(C.av_hwdevice_ctx_create(&hdc.c, (C.enum_AVHWDeviceType)(t), deviceC, optionsC, 0)); err != nil {
return nil, err
}
return &hdc, nil
}

View File

@@ -1,36 +0,0 @@
package astiav
//#cgo pkg-config: libavutil
//#include <libavutil/hwcontext.h>
import "C"
import (
"unsafe"
)
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavutil/hwcontext.h#L27
type HardwareDeviceType C.enum_AVHWDeviceType
const (
HardwareDeviceTypeCUDA = HardwareDeviceType(C.AV_HWDEVICE_TYPE_CUDA)
HardwareDeviceTypeD3D11VA = HardwareDeviceType(C.AV_HWDEVICE_TYPE_D3D11VA)
HardwareDeviceTypeDRM = HardwareDeviceType(C.AV_HWDEVICE_TYPE_DRM)
HardwareDeviceTypeDXVA2 = HardwareDeviceType(C.AV_HWDEVICE_TYPE_DXVA2)
HardwareDeviceTypeMediaCodec = HardwareDeviceType(C.AV_HWDEVICE_TYPE_MEDIACODEC)
HardwareDeviceTypeNone = HardwareDeviceType(C.AV_HWDEVICE_TYPE_NONE)
HardwareDeviceTypeOpenCL = HardwareDeviceType(C.AV_HWDEVICE_TYPE_OPENCL)
HardwareDeviceTypeQSV = HardwareDeviceType(C.AV_HWDEVICE_TYPE_QSV)
HardwareDeviceTypeVAAPI = HardwareDeviceType(C.AV_HWDEVICE_TYPE_VAAPI)
HardwareDeviceTypeVDPAU = HardwareDeviceType(C.AV_HWDEVICE_TYPE_VDPAU)
HardwareDeviceTypeVideoToolbox = HardwareDeviceType(C.AV_HWDEVICE_TYPE_VIDEOTOOLBOX)
HardwareDeviceTypeVulkan = HardwareDeviceType(C.AV_HWDEVICE_TYPE_VULKAN)
)
func (t HardwareDeviceType) String() string {
return C.GoString(C.av_hwdevice_get_type_name((C.enum_AVHWDeviceType)(t)))
}
func FindHardwareDeviceTypeByName(n string) HardwareDeviceType {
cn := C.CString(n)
defer C.free(unsafe.Pointer(cn))
return HardwareDeviceType(C.av_hwdevice_find_type_by_name(cn))
}

View File

@@ -1,12 +0,0 @@
package astiav
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestHardwareDeviceType(t *testing.T) {
require.Equal(t, "cuda", HardwareDeviceTypeCUDA.String())
require.Equal(t, FindHardwareDeviceTypeByName("cuda"), HardwareDeviceTypeCUDA)
}

View File

@@ -1,41 +0,0 @@
package astiav
//#cgo pkg-config: libavformat
//#include <libavformat/avformat.h>
import "C"
import "unsafe"
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavformat/avformat.h#L650
type InputFormat struct {
c *C.struct_AVInputFormat
}
func newInputFormatFromC(c *C.struct_AVInputFormat) *InputFormat {
if c == nil {
return nil
}
return &InputFormat{c: c}
}
func FindInputFormat(name string) *InputFormat {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
return newInputFormatFromC(C.av_find_input_format(cname))
}
func (f *InputFormat) Flags() IOFormatFlags {
return IOFormatFlags(f.c.flags)
}
func (f *InputFormat) Name() string {
return C.GoString(f.c.name)
}
// LongName Description of the format, meant to be more human-readable than Name.
func (f *InputFormat) LongName() string {
return C.GoString(f.c.long_name)
}
func (f *InputFormat) String() string {
return f.Name()
}

View File

@@ -1,16 +0,0 @@
package astiav
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestInputFormat(t *testing.T) {
formatName := "rawvideo"
inputFormat := FindInputFormat(formatName)
require.NotNil(t, inputFormat)
require.Equal(t, formatName, inputFormat.Name())
require.Equal(t, formatName, inputFormat.String())
require.Equal(t, "raw video", inputFormat.LongName())
}

View File

@@ -1,28 +0,0 @@
package astiav
//#cgo pkg-config: libavutil
//#include <libavutil/intreadwrite.h>
/*
uint32_t astiavRL32(uint8_t *i) {
return AV_RL32(i);
}
uint32_t astiavRL32WithOffset(uint8_t *i, int o) {
return AV_RL32(i+o);
}
*/
import "C"
import "unsafe"
func RL32(i []byte) uint32 {
if len(i) == 0 {
return 0
}
return uint32(C.astiavRL32((*C.uint8_t)(unsafe.Pointer(&i[0]))))
}
func RL32WithOffset(i []byte, offset uint) uint32 {
if len(i) == 0 {
return 0
}
return uint32(C.astiavRL32WithOffset((*C.uint8_t)(unsafe.Pointer(&i[0])), C.int(offset)))
}

View File

@@ -1,15 +0,0 @@
package astiav
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestIntReadWrite(t *testing.T) {
is := []uint8{1, 2, 3, 4, 5, 6, 7, 8}
require.Equal(t, uint32(0), RL32([]byte{}))
require.Equal(t, uint32(0x4030201), RL32(is))
require.Equal(t, uint32(0), RL32WithOffset([]byte{}, 4))
require.Equal(t, uint32(0x8070605), RL32WithOffset(is, 4))
}

View File

@@ -1,106 +0,0 @@
package main
import (
"fmt"
"log"
"os"
"path/filepath"
"text/template"
)
type listItem struct {
Name string
Suffix string
}
var list = []listItem{
{Name: "Buffersink"},
{Name: "Buffersrc"},
{Name: "CodecContext"},
{Name: "CodecContext", Suffix: "2"},
{Name: "CodecHardwareConfigMethod"},
{Name: "Dictionary"},
{Name: "FilterCommand"},
{Name: "FormatContext"},
{Name: "FormatContextCtx"},
{Name: "FormatEvent"},
{Name: "IOContext"},
{Name: "IOFormat"},
{Name: "Packet"},
{Name: "Seek"},
{Name: "SoftwareScaleContext"},
{Name: "StreamEvent"},
}
var tmpl = `// Code generated by astiav. DO NOT EDIT.
package astiav
import (
"github.com/asticode/go-astikit"
)
{{ range $val := . }}
type {{ $val.Name }}Flags{{ $val.Suffix }} astikit.BitFlags
func New{{ $val.Name }}Flags{{ $val.Suffix }}(fs ...{{ $val.Name }}Flag{{ $val.Suffix }}) {{ $val.Name }}Flags{{ $val.Suffix }} {
o := {{ $val.Name }}Flags{{ $val.Suffix }}(0)
for _, f := range fs {
o = o.Add(f)
}
return o
}
func (fs {{ $val.Name }}Flags{{ $val.Suffix }}) Add(f {{ $val.Name }}Flag{{ $val.Suffix }}) {{ $val.Name }}Flags{{ $val.Suffix }} {
return {{ $val.Name }}Flags{{ $val.Suffix }}(astikit.BitFlags(fs).Add(uint64(f)))
}
func (fs {{ $val.Name }}Flags{{ $val.Suffix }}) Del(f {{ $val.Name }}Flag{{ $val.Suffix }}) {{ $val.Name }}Flags{{ $val.Suffix }} {
return {{ $val.Name }}Flags{{ $val.Suffix }}(astikit.BitFlags(fs).Del(uint64(f)))
}
func (fs {{ $val.Name }}Flags{{ $val.Suffix }}) Has(f {{ $val.Name }}Flag{{ $val.Suffix }}) bool { return astikit.BitFlags(fs).Has(uint64(f)) }
{{ end }}`
var tmplTest = `// Code generated by astiav. DO NOT EDIT.
package astiav
import (
"testing"
"github.com/stretchr/testify/require"
)
{{ range $val := . }}
func Test{{ $val.Name }}Flags{{ $val.Suffix }}(t *testing.T) {
fs := New{{ $val.Name }}Flags{{ $val.Suffix }}({{ $val.Name }}Flag{{ $val.Suffix }}(1))
require.True(t, fs.Has({{ $val.Name }}Flag{{ $val.Suffix }}(1)))
fs = fs.Add({{ $val.Name }}Flag{{ $val.Suffix }}(2))
require.True(t, fs.Has({{ $val.Name }}Flag{{ $val.Suffix }}(2)))
fs = fs.Del({{ $val.Name }}Flag{{ $val.Suffix }}(2))
require.False(t, fs.Has({{ $val.Name }}Flag{{ $val.Suffix }}(2)))
}
{{ end }}`
func main() {
dir, err := os.Getwd()
if err != nil {
log.Fatal(fmt.Errorf("main: getting working directory failed: %w", err))
}
f, err := os.Create(filepath.Join(dir, "flags.go"))
if err != nil {
log.Fatal(fmt.Errorf("main: creating file failed: %w", err))
}
defer f.Close()
if err = template.Must(template.New("tmpl").Parse(tmpl)).Execute(f, list); err != nil {
log.Fatal(fmt.Errorf("main: executing template failed: %w", err))
}
ft, err := os.Create(filepath.Join(dir, "flags_test.go"))
if err != nil {
log.Fatal(fmt.Errorf("main: creating test file failed: %w", err))
}
defer ft.Close()
if err = template.Must(template.New("tmpl").Parse(tmplTest)).Execute(ft, list); err != nil {
log.Fatal(fmt.Errorf("main: executing template failed: %w", err))
}
}

View File

@@ -1,39 +0,0 @@
FROM arm32v7/debian:12.5
RUN apt-get update
RUN apt-get install -y \
build-essential \
git \
pkg-config \
libpng-dev
RUN \
mkdir -p /opt/ffmpeg/src
WORKDIR /opt/ffmpeg/src
RUN \
git clone https://github.com/FFmpeg/FFmpeg /opt/ffmpeg/src && \
git checkout n5.1.2
RUN \
./configure --prefix=.. && \
make && \
make install
ADD https://dl.google.com/go/go1.22.0.linux-amd64.tar.gz /tmp/go.tar.gz
RUN tar -C /opt -xzf /tmp/go.tar.gz
ENV GOARCH=arm
ENV GOARM=7
ENV GOCACHE=/opt/gocache
ENV GOMODCACHE=/opt/gomodcache
ENV CGO_LDFLAGS=-L/opt/ffmpeg/lib/
ENV CGO_CFLAGS=-I/opt/ffmpeg/include/
ENV PKG_CONFIG_PATH=/opt/ffmpeg/lib/pkgconfig
ENV CGO_ENABLED=1
WORKDIR /opt/astiav
CMD ["/opt/go/bin/go", "test"]

View File

@@ -1,17 +0,0 @@
#include "io_context.h"
#include <stdint.h>
int astiavIOContextReadFunc(void *opaque, uint8_t *buf, int buf_size)
{
return goAstiavIOContextReadFunc(opaque, buf, buf_size);
}
int64_t astiavIOContextSeekFunc(void *opaque, int64_t offset, int whence)
{
return goAstiavIOContextSeekFunc(opaque, offset, whence);
}
int astiavIOContextWriteFunc(void *opaque, uint8_t *buf, int buf_size)
{
return goAstiavIOContextWriteFunc(opaque, buf, buf_size);
}

View File

@@ -1,286 +0,0 @@
package astiav
//#cgo pkg-config: libavformat
//#include <libavformat/avformat.h>
//#include "io_context.h"
import "C"
import (
"errors"
"fmt"
"sync"
"unsafe"
)
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavformat/avio.h#L161
type IOContext struct {
c *C.struct_AVIOContext
handlerID unsafe.Pointer
}
func newIOContextFromC(c *C.struct_AVIOContext) *IOContext {
if c == nil {
return nil
}
ic := &IOContext{c: c}
classers.set(ic)
return ic
}
var _ Classer = (*IOContext)(nil)
type IOContextReadFunc func(b []byte) (n int, err error)
type IOContextSeekFunc func(offset int64, whence int) (n int64, err error)
type IOContextWriteFunc func(b []byte) (n int, err error)
func AllocIOContext(bufferSize int, readFunc IOContextReadFunc, seekFunc IOContextSeekFunc, writeFunc IOContextWriteFunc) (ic *IOContext, err error) {
// Invalid buffer size
if bufferSize <= 0 {
err = errors.New("astiav: buffer size <= 0")
return
}
// Alloc buffer
buffer := C.av_malloc(C.size_t(bufferSize))
if buffer == nil {
err = errors.New("astiav: allocating buffer failed")
return
}
// Make sure buffer is freed in case of error
defer func() {
if err != nil {
C.av_free(buffer)
}
}()
// Since go doesn't allow c to store pointers to go data, we need to create this C pointer
handlerID := C.malloc(C.size_t(1))
if handlerID == nil {
err = errors.New("astiav: allocating handler id failed")
return
}
// Make sure handler id is freed in case of error
defer func() {
if err != nil {
C.free(handlerID)
}
}()
// Get callbacks
var cReadFunc, cSeekFunc, cWriteFunc *[0]byte
if readFunc != nil {
cReadFunc = (*[0]byte)(C.astiavIOContextReadFunc)
}
if seekFunc != nil {
cSeekFunc = (*[0]byte)(C.astiavIOContextSeekFunc)
}
if writeFunc != nil {
cWriteFunc = (*[0]byte)(C.astiavIOContextWriteFunc)
}
// Alloc io context
cic := C.avio_alloc_context((*C.uchar)(buffer), C.int(bufferSize), 1, handlerID, cReadFunc, cWriteFunc, cSeekFunc)
if cic == nil {
err = errors.New("astiav: allocating io context failed: %w")
return
}
// Create io context
ic = newIOContextFromC(cic)
// Store handler
ic.handlerID = handlerID
ioContextHandlers.set(handlerID, &ioContextHandler{
r: readFunc,
s: seekFunc,
w: writeFunc,
})
return
}
func OpenIOContext(filename string, flags IOContextFlags) (*IOContext, error) {
cfi := C.CString(filename)
defer C.free(unsafe.Pointer(cfi))
var c *C.struct_AVIOContext
if err := newError(C.avio_open(&c, cfi, C.int(flags))); err != nil {
return nil, err
}
return newIOContextFromC(c), nil
}
func (ic *IOContext) Class() *Class {
return newClassFromC(unsafe.Pointer(ic.c))
}
func (ic *IOContext) Close() error {
classers.del(ic)
if ic.c != nil {
return newError(C.avio_closep(&ic.c))
}
return nil
}
func (ic *IOContext) Free() {
classers.del(ic)
if ic.c != nil {
if ic.c.buffer != nil {
C.av_freep(unsafe.Pointer(&ic.c.buffer))
}
if ic.handlerID != nil {
C.free(ic.handlerID)
ic.handlerID = nil
}
C.avio_context_free(&ic.c)
}
return
}
func (ic *IOContext) Read(b []byte) (n int, err error) {
// Nothing to read
if b == nil || len(b) <= 0 {
return
}
// Alloc buffer
buf := C.av_malloc(C.size_t(len(b)))
if buf == nil {
err = errors.New("astiav: allocating buffer failed")
return
}
// Make sure buffer is freed
defer C.av_free(buf)
// Read
ret := C.avio_read_partial(ic.c, (*C.uchar)(unsafe.Pointer(buf)), C.int(len(b)))
if err = newError(ret); err != nil {
err = fmt.Errorf("astiav: reading failed: %w", err)
return
}
// Copy
C.memcpy(unsafe.Pointer(&b[0]), unsafe.Pointer(buf), C.size_t(ret))
n = int(ret)
return
}
func (ic *IOContext) Seek(offset int64, whence int) (int64, error) {
ret := C.avio_seek(ic.c, C.int64_t(offset), C.int(whence))
if err := newError(C.int(ret)); err != nil {
return 0, err
}
return int64(ret), nil
}
func (ic *IOContext) Write(b []byte) {
// Nothing to write
if b == nil || len(b) <= 0 {
return
}
// Write
C.avio_write(ic.c, (*C.uchar)(unsafe.Pointer(&b[0])), C.int(len(b)))
}
func (ic *IOContext) Flush() {
C.avio_flush(ic.c)
}
type ioContextHandler struct {
r IOContextReadFunc
s IOContextSeekFunc
w IOContextWriteFunc
}
var ioContextHandlers = newIOContextHandlerPool()
type ioContextHandlerPool struct {
m sync.Mutex
p map[unsafe.Pointer]*ioContextHandler
}
func newIOContextHandlerPool() *ioContextHandlerPool {
return &ioContextHandlerPool{p: make(map[unsafe.Pointer]*ioContextHandler)}
}
func (p *ioContextHandlerPool) set(id unsafe.Pointer, h *ioContextHandler) {
p.m.Lock()
defer p.m.Unlock()
p.p[id] = h
}
func (p *ioContextHandlerPool) get(id unsafe.Pointer) (h *ioContextHandler, ok bool) {
p.m.Lock()
defer p.m.Unlock()
h, ok = p.p[id]
return
}
//export goAstiavIOContextReadFunc
func goAstiavIOContextReadFunc(opaque unsafe.Pointer, buf *C.uint8_t, bufSize C.int) C.int {
// Get handler
h, ok := ioContextHandlers.get(opaque)
if !ok {
return C.AVERROR_UNKNOWN
}
// Create go buffer
b := make([]byte, int(bufSize), int(bufSize))
// Read
n, err := h.r(b)
if err != nil {
var e Error
if errors.As(err, &e) {
return C.int(e)
}
return C.AVERROR_UNKNOWN
}
// Copy
C.memcpy(unsafe.Pointer(buf), unsafe.Pointer(&b[0]), C.size_t(n))
return C.int(n)
}
//export goAstiavIOContextSeekFunc
func goAstiavIOContextSeekFunc(opaque unsafe.Pointer, offset C.int64_t, whence C.int) C.int64_t {
// Get handler
h, ok := ioContextHandlers.get(opaque)
if !ok {
return C.AVERROR_UNKNOWN
}
// Seek
n, err := h.s(int64(offset), int(whence))
if err != nil {
var e Error
if errors.As(err, &e) {
return C.int64_t(e)
}
return C.int64_t(C.AVERROR_UNKNOWN)
}
return C.int64_t(n)
}
//export goAstiavIOContextWriteFunc
func goAstiavIOContextWriteFunc(opaque unsafe.Pointer, buf *C.uint8_t, bufSize C.int) C.int {
// Get handler
h, ok := ioContextHandlers.get(opaque)
if !ok {
return C.AVERROR_UNKNOWN
}
// Write
n, err := h.w(C.GoBytes(unsafe.Pointer(buf), bufSize))
if err != nil {
var e Error
if errors.As(err, &e) {
return C.int(e)
}
return C.AVERROR_UNKNOWN
}
return C.int(n)
}

View File

@@ -1,9 +0,0 @@
#include <stdint.h>
extern int goAstiavIOContextReadFunc(void *opaque, uint8_t *buf, int buf_size);
extern int64_t goAstiavIOContextSeekFunc(void *opaque, int64_t offset, int whence);
extern int goAstiavIOContextWriteFunc(void *opaque, uint8_t *buf, int buf_size);
int astiavIOContextReadFunc(void *opaque, uint8_t *buf, int buf_size);
int64_t astiavIOContextSeekFunc(void *opaque, int64_t offset, int whence);
int astiavIOContextWriteFunc(void *opaque, uint8_t *buf, int buf_size);

View File

@@ -1,16 +0,0 @@
package astiav
//#cgo pkg-config: libavformat
//#include <libavformat/avformat.h>
import "C"
type IOContextFlag int64
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavformat/avio.h#L621
const (
IOContextFlagRead = IOContextFlag(C.AVIO_FLAG_READ)
IOContextFlagWrite = IOContextFlag(C.AVIO_FLAG_WRITE)
IOContextFlagReadWrite = IOContextFlag(C.AVIO_FLAG_READ_WRITE)
IOContextFlagNonBlock = IOContextFlag(C.AVIO_FLAG_NONBLOCK)
IOContextFlagDirect = IOContextFlag(C.AVIO_FLAG_DIRECT)
)

View File

@@ -1,57 +0,0 @@
package astiav
import (
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/require"
)
func TestIOContext(t *testing.T) {
var seeked bool
rb := []byte("read")
wb := []byte("write")
var written []byte
c, err := AllocIOContext(8, func(b []byte) (int, error) {
copy(b, rb)
return len(rb), nil
}, func(offset int64, whence int) (n int64, err error) {
seeked = true
return offset, nil
}, func(b []byte) (int, error) {
written = make([]byte, len(b))
copy(written, b)
return len(b), nil
})
require.NoError(t, err)
defer c.Free()
b := make([]byte, 6)
n, err := c.Read(b)
require.NoError(t, err)
require.Equal(t, 4, n)
require.Equal(t, rb, b[:n])
_, err = c.Seek(2, 0)
require.NoError(t, err)
require.True(t, seeked)
c.Write(wb)
c.Flush()
require.Equal(t, wb, written)
}
func TestOpenIOContext(t *testing.T) {
path := filepath.Join(t.TempDir(), "iocontext.txt")
c, err := OpenIOContext(path, NewIOContextFlags(IOContextFlagWrite))
require.NoError(t, err)
cl := c.Class()
require.NotNil(t, cl)
require.Equal(t, "AVIOContext", cl.Name())
c.Write(nil)
c.Write([]byte("test"))
require.NoError(t, c.Close())
b, err := os.ReadFile(path)
require.NoError(t, err)
require.Equal(t, "test", string(b))
err = os.Remove(path)
require.NoError(t, err)
}

View File

@@ -1,28 +0,0 @@
package astiav
//#cgo pkg-config: libavformat
//#include <libavformat/avformat.h>
import "C"
type IOFormatFlag int64
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavformat/avformat.h#L464
const (
IOFormatFlagNofile = IOFormatFlag(C.AVFMT_NOFILE)
IOFormatFlagNeednumber = IOFormatFlag(C.AVFMT_NEEDNUMBER)
IOFormatFlagShowIds = IOFormatFlag(C.AVFMT_SHOW_IDS)
IOFormatFlagGlobalheader = IOFormatFlag(C.AVFMT_GLOBALHEADER)
IOFormatFlagNotimestamps = IOFormatFlag(C.AVFMT_NOTIMESTAMPS)
IOFormatFlagGenericIndex = IOFormatFlag(C.AVFMT_GENERIC_INDEX)
IOFormatFlagTsDiscont = IOFormatFlag(C.AVFMT_TS_DISCONT)
IOFormatFlagVariableFps = IOFormatFlag(C.AVFMT_VARIABLE_FPS)
IOFormatFlagNodimensions = IOFormatFlag(C.AVFMT_NODIMENSIONS)
IOFormatFlagNostreams = IOFormatFlag(C.AVFMT_NOSTREAMS)
IOFormatFlagNobinsearch = IOFormatFlag(C.AVFMT_NOBINSEARCH)
IOFormatFlagNogensearch = IOFormatFlag(C.AVFMT_NOGENSEARCH)
IOFormatFlagNoByteSeek = IOFormatFlag(C.AVFMT_NO_BYTE_SEEK)
IOFormatFlagAllowFlush = IOFormatFlag(C.AVFMT_ALLOW_FLUSH)
IOFormatFlagTsNonstrict = IOFormatFlag(C.AVFMT_TS_NONSTRICT)
IOFormatFlagTsNegative = IOFormatFlag(C.AVFMT_TS_NEGATIVE)
IOFormatFlagSeekToPts = IOFormatFlag(C.AVFMT_SEEK_TO_PTS)
)

View File

@@ -1,40 +0,0 @@
package astiav
//#cgo pkg-config: libavformat
//#include <libavformat/avio.h>
/*
int astiavInterruptCallback(void *ret)
{
return *((int*)ret);
}
AVIOInterruptCB astiavNewInterruptCallback(int *ret)
{
AVIOInterruptCB c = { astiavInterruptCallback, ret };
return c;
}
*/
import "C"
type IOInterrupter interface {
Interrupt()
Resume()
}
type defaultIOInterrupter struct {
c C.struct_AVIOInterruptCB
i C.int
}
func newDefaultIOInterrupter() *defaultIOInterrupter {
i := &defaultIOInterrupter{}
i.c = C.astiavNewInterruptCallback(&i.i)
return i
}
func (i *defaultIOInterrupter) Interrupt() {
i.i = 1
}
func (i *defaultIOInterrupter) Resume() {
i.i = 0
}

View File

@@ -1,16 +0,0 @@
package astiav
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestDefaultIOInterrupter(t *testing.T) {
ii := newDefaultIOInterrupter()
require.Equal(t, 0, int(ii.i))
ii.Interrupt()
require.Equal(t, 1, int(ii.i))
ii.Resume()
require.Equal(t, 0, int(ii.i))
}

View File

@@ -1,12 +0,0 @@
package astiav
//#cgo pkg-config: libavcodec
//#include <libavcodec/avcodec.h>
import "C"
type Level int
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavcodec/avcodec.h#L1652
const (
LevelUnknown = Level(C.FF_LEVEL_UNKNOWN)
)

View File

@@ -1,105 +0,0 @@
package astiav
//#cgo pkg-config: libavutil
//#include <libavutil/log.h>
//#include <stdio.h>
//#include <stdlib.h>
/*
extern void goAstiavLogCallback(void* ptr, int level, char* fmt, char* msg);
static inline void astiavLogCallback(void *ptr, int level, const char *fmt, va_list vl)
{
if (level > av_log_get_level()) return;
char msg[1024];
vsprintf(msg, fmt, vl);
goAstiavLogCallback(ptr, level, (char*)(fmt), msg);
}
static inline void astiavSetLogCallback()
{
av_log_set_callback(astiavLogCallback);
}
static inline void astiavResetLogCallback()
{
av_log_set_callback(av_log_default_callback);
}
static inline void astiavLog(void* ptr, int level, const char *fmt, char* arg)
{
av_log(ptr, level, fmt, arg);
}
*/
import "C"
import (
"unsafe"
)
type LogLevel int
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavutil/log.h#L162
const (
LogLevelQuiet = LogLevel(C.AV_LOG_QUIET)
LogLevelPanic = LogLevel(C.AV_LOG_PANIC)
LogLevelFatal = LogLevel(C.AV_LOG_FATAL)
LogLevelError = LogLevel(C.AV_LOG_ERROR)
LogLevelWarning = LogLevel(C.AV_LOG_WARNING)
LogLevelInfo = LogLevel(C.AV_LOG_INFO)
LogLevelVerbose = LogLevel(C.AV_LOG_VERBOSE)
LogLevelDebug = LogLevel(C.AV_LOG_DEBUG)
)
func SetLogLevel(l LogLevel) {
C.av_log_set_level(C.int(l))
}
func GetLogLevel() LogLevel {
return LogLevel(C.av_log_get_level())
}
type LogCallback func(c Classer, l LogLevel, fmt, msg string)
var logCallback LogCallback
func SetLogCallback(c LogCallback) {
logCallback = c
C.astiavSetLogCallback()
}
//export goAstiavLogCallback
func goAstiavLogCallback(ptr unsafe.Pointer, level C.int, fmt, msg *C.char) {
// No callback
if logCallback == nil {
return
}
// Get classer
var c Classer
if ptr != nil {
var ok bool
if c, ok = classers.get(ptr); !ok {
c = newUnknownClasser(ptr)
}
}
// Callback
logCallback(c, LogLevel(level), C.GoString(fmt), C.GoString(msg))
}
func ResetLogCallback() {
C.astiavResetLogCallback()
}
func Log(c Classer, l LogLevel, fmt string, args ...string) {
fmtc := C.CString(fmt)
defer C.free(unsafe.Pointer(fmtc))
argc := (*C.char)(nil)
if len(args) > 0 {
argc = C.CString(args[0])
defer C.free(unsafe.Pointer(argc))
}
var ptr unsafe.Pointer
if c != nil {
if cl := c.Class(); cl != nil {
ptr = cl.ptr
}
}
C.astiavLog(ptr, C.int(l), fmtc, argc)
}

View File

@@ -1,73 +0,0 @@
package astiav
import (
"testing"
"github.com/stretchr/testify/require"
)
type logItem struct {
c Classer
fmt string
l LogLevel
msg string
}
func TestLog(t *testing.T) {
var lis []logItem
SetLogLevel(LogLevelWarning)
require.Equal(t, LogLevelWarning, GetLogLevel())
SetLogCallback(func(c Classer, l LogLevel, fmt, msg string) {
lis = append(lis, logItem{
c: c,
fmt: fmt,
l: l,
msg: msg,
})
})
f := AllocFilterGraph()
defer f.Free()
Log(f, LogLevelInfo, "info")
Log(f, LogLevelWarning, "warning %s", "arg")
Log(f, LogLevelError, "error")
Log(f, LogLevelFatal, "fatal")
require.Equal(t, []logItem{
{
c: f,
fmt: "warning %s",
l: LogLevelWarning,
msg: "warning arg",
},
{
c: f,
fmt: "error",
l: LogLevelError,
msg: "error",
},
{
c: f,
fmt: "fatal",
l: LogLevelFatal,
msg: "fatal",
},
}, lis)
ResetLogCallback()
lis = []logItem{}
Log(nil, LogLevelError, "test error log\n")
require.Equal(t, []logItem{}, lis)
lcs := []Classer{}
SetLogCallback(func(c Classer, l LogLevel, fmt, msg string) {
if c != nil {
lcs = append(lcs, c)
}
})
classers.del(f)
lcs = []Classer{}
Log(f, LogLevelWarning, "")
require.Len(t, lcs, 1)
require.IsType(t, &UnknownClasser{}, lcs[0])
}

View File

@@ -1,13 +0,0 @@
package astiav
//#cgo pkg-config: libavutil
//#include <libavutil/mathematics.h>
import "C"
func RescaleQ(a int64, b Rational, c Rational) int64 {
return int64(C.av_rescale_q(C.int64_t(a), b.c, c.c))
}
func RescaleQRnd(a int64, b Rational, c Rational, r Rounding) int64 {
return int64(C.av_rescale_q_rnd(C.int64_t(a), b.c, c.c, C.enum_AVRounding(r)))
}

View File

@@ -1,13 +0,0 @@
package astiav
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestMathematics(t *testing.T) {
require.Equal(t, int64(1000), RescaleQ(100, NewRational(1, 100), NewRational(1, 1000)))
require.Equal(t, int64(0), RescaleQRnd(1, NewRational(1, 100), NewRational(1, 10), RoundingDown))
require.Equal(t, int64(1), RescaleQRnd(1, NewRational(1, 100), NewRational(1, 10), RoundingUp))
}

View File

@@ -1,23 +0,0 @@
package astiav
//#cgo pkg-config: libavcodec libavutil
//#include <libavcodec/avcodec.h>
//#include <libavutil/avutil.h>
import "C"
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavutil/avutil.h#L199
type MediaType C.enum_AVMediaType
const (
MediaTypeAttachment = MediaType(C.AVMEDIA_TYPE_ATTACHMENT)
MediaTypeAudio = MediaType(C.AVMEDIA_TYPE_AUDIO)
MediaTypeData = MediaType(C.AVMEDIA_TYPE_DATA)
MediaTypeNb = MediaType(C.AVMEDIA_TYPE_NB)
MediaTypeSubtitle = MediaType(C.AVMEDIA_TYPE_SUBTITLE)
MediaTypeUnknown = MediaType(C.AVMEDIA_TYPE_UNKNOWN)
MediaTypeVideo = MediaType(C.AVMEDIA_TYPE_VIDEO)
)
func (t MediaType) String() string {
return C.GoString(C.av_get_media_type_string((C.enum_AVMediaType)(t)))
}

View File

@@ -1,11 +0,0 @@
package astiav
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestMediaType(t *testing.T) {
require.Equal(t, "video", MediaTypeVideo.String())
}

View File

@@ -1,41 +0,0 @@
package astiav
//#cgo pkg-config: libavformat
//#include <libavformat/avformat.h>
import "C"
import "unsafe"
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavformat/avformat.h#L503
type OutputFormat struct {
c *C.struct_AVOutputFormat
}
func newOutputFormatFromC(c *C.struct_AVOutputFormat) *OutputFormat {
if c == nil {
return nil
}
return &OutputFormat{c: c}
}
func FindOutputFormat(name string) *OutputFormat {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
return newOutputFormatFromC(C.av_guess_format(cname, nil, nil))
}
func (f *OutputFormat) Flags() IOFormatFlags {
return IOFormatFlags(f.c.flags)
}
func (f *OutputFormat) Name() string {
return C.GoString(f.c.name)
}
// LongName Description of the format, meant to be more human-readable than Name.
func (f *OutputFormat) LongName() string {
return C.GoString(f.c.long_name)
}
func (f *OutputFormat) String() string {
return f.Name()
}

View File

@@ -1,16 +0,0 @@
package astiav
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestOutputFormat(t *testing.T) {
formatName := "rawvideo"
outputFormat := FindOutputFormat(formatName)
require.NotNil(t, outputFormat)
require.Equal(t, formatName, outputFormat.Name())
require.Equal(t, formatName, outputFormat.String())
require.Equal(t, "raw video", outputFormat.LongName())
}

Some files were not shown because too many files have changed in this diff Show More