mirror of
https://github.com/flavioribeiro/donut.git
synced 2025-09-26 19:11:11 +08:00
remove local go astiav impl
This commit is contained in:
@@ -20,8 +20,6 @@ RUN apt-get clean && apt-get update && \
|
||||
ENV GOPROXY=direct
|
||||
|
||||
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
|
||||
RUN go build .
|
||||
CMD ["/usr/src/app/donut/donut", "--enable-ice-mux=true"]
|
5
go.mod
generated
5
go.mod
generated
@@ -2,11 +2,8 @@ module github.com/flavioribeiro/donut
|
||||
|
||||
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 (
|
||||
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/kelseyhightower/envconfig v1.4.0
|
||||
github.com/pion/webrtc/v3 v3.1.47
|
||||
|
2
go.sum
generated
2
go.sum
generated
@@ -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/go.mod h1:h4ly7idim1tNhaVkdVBeXQZEE3L0xblP7fCWbgwipF0=
|
||||
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
|
||||
|
@@ -75,7 +75,7 @@ type streamContext struct {
|
||||
encPkt *astiav.Packet
|
||||
|
||||
// Bit stream filter
|
||||
bsfContext *astiav.BSFContext
|
||||
bsfContext *astiav.BitStreamFilterContext
|
||||
}
|
||||
|
||||
type libAVParams struct {
|
||||
@@ -540,7 +540,7 @@ func (c *LibAVFFmpegStreamer) prepareBitStreamFilters(p *libAVParams, closer *as
|
||||
}
|
||||
|
||||
var err error
|
||||
s.bsfContext, err = astiav.AllocBitStreamContext(bsf)
|
||||
s.bsfContext, err = astiav.AllocBitStreamFilterContext(bsf)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
if err := s.bsfContext.Init(); err != nil {
|
||||
if err := s.bsfContext.Initialize(); err != nil {
|
||||
return fmt.Errorf("error while initiating %w", err)
|
||||
}
|
||||
}
|
||||
|
@@ -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.
|
@@ -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)
|
@@ -1,57 +0,0 @@
|
||||
[](http://goreportcard.com/report/github.com/asticode/go-astiav)
|
||||
[](https://godoc.org/github.com/asticode/go-astiav)
|
||||
[](https://github.com/asticode/go-astiav/actions/workflows/test.yml)
|
||||
[](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`.
|
@@ -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
|
||||
}
|
@@ -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
|
||||
// }
|
@@ -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)
|
||||
)
|
@@ -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)
|
||||
)
|
@@ -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
|
||||
}
|
@@ -1,9 +0,0 @@
|
||||
package astiav
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestBytes(t *testing.T) {
|
||||
// TODO Test stringFromC
|
||||
// TODO Test bytesFromC
|
||||
// TODO Test bytesToC
|
||||
}
|
@@ -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
|
||||
}
|
@@ -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))
|
||||
}
|
@@ -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)
|
||||
)
|
@@ -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
|
||||
}
|
@@ -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)
|
||||
)
|
@@ -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))
|
||||
}
|
@@ -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
|
||||
}
|
@@ -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))
|
||||
|
||||
}
|
@@ -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)
|
||||
)
|
@@ -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
|
||||
}
|
@@ -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)
|
||||
}
|
@@ -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)
|
||||
)
|
@@ -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()
|
||||
}
|
@@ -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())
|
||||
}
|
@@ -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))
|
||||
}
|
@@ -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())
|
||||
}
|
@@ -1,3 +0,0 @@
|
||||
package astiav
|
||||
|
||||
type CodecTag uint32
|
@@ -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)
|
||||
}
|
@@ -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)
|
||||
)
|
@@ -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)
|
||||
)
|
@@ -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)
|
||||
)
|
@@ -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)
|
||||
)
|
@@ -1,9 +0,0 @@
|
||||
package astiav
|
||||
|
||||
//#cgo pkg-config: libavdevice
|
||||
//#include <libavdevice/avdevice.h>
|
||||
import "C"
|
||||
|
||||
func RegisterAllDevices() {
|
||||
C.avdevice_register_all()
|
||||
}
|
@@ -1,9 +0,0 @@
|
||||
package astiav
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDevice(t *testing.T) {
|
||||
RegisterAllDevices()
|
||||
}
|
@@ -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))
|
||||
})
|
||||
}
|
@@ -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)
|
||||
}
|
@@ -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)
|
||||
)
|
@@ -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())
|
||||
}
|
@@ -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]))))
|
||||
}
|
@@ -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())
|
||||
}
|
@@ -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)
|
||||
}
|
@@ -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))
|
||||
}
|
@@ -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")
|
||||
}
|
@@ -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
|
||||
}
|
@@ -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")
|
||||
}
|
@@ -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")
|
||||
}
|
@@ -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")
|
||||
}
|
@@ -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
|
||||
}
|
@@ -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()
|
||||
}
|
@@ -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)
|
||||
)
|
@@ -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
|
||||
}
|
@@ -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
|
||||
}
|
@@ -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
|
||||
}
|
@@ -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
|
||||
}
|
@@ -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)
|
||||
}
|
@@ -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())
|
||||
}
|
@@ -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)) }
|
@@ -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)))
|
||||
}
|
@@ -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)))
|
||||
})
|
||||
}
|
@@ -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)
|
||||
)
|
@@ -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)
|
||||
)
|
@@ -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
|
||||
}
|
@@ -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)
|
||||
)
|
@@ -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)
|
||||
}
|
@@ -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()
|
||||
}
|
@@ -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)
|
||||
}
|
@@ -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)
|
||||
}
|
@@ -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)
|
||||
)
|
@@ -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)
|
||||
}
|
16
local-tmp-go-astiav/go.mod
generated
16
local-tmp-go-astiav/go.mod
generated
@@ -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
|
13
local-tmp-go-astiav/go.sum
generated
13
local-tmp-go-astiav/go.sum
generated
@@ -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=
|
@@ -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
|
||||
}
|
@@ -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))
|
||||
}
|
@@ -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)
|
||||
}
|
@@ -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()
|
||||
}
|
@@ -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())
|
||||
}
|
@@ -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)))
|
||||
}
|
@@ -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))
|
||||
}
|
@@ -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))
|
||||
}
|
||||
}
|
@@ -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"]
|
@@ -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);
|
||||
}
|
@@ -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)
|
||||
}
|
@@ -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);
|
@@ -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)
|
||||
)
|
@@ -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)
|
||||
}
|
@@ -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)
|
||||
)
|
@@ -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
|
||||
}
|
@@ -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))
|
||||
}
|
@@ -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)
|
||||
)
|
@@ -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)
|
||||
}
|
@@ -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])
|
||||
}
|
@@ -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)))
|
||||
}
|
@@ -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))
|
||||
}
|
@@ -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)))
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
package astiav
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMediaType(t *testing.T) {
|
||||
require.Equal(t, "video", MediaTypeVideo.String())
|
||||
}
|
@@ -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()
|
||||
}
|
@@ -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
Reference in New Issue
Block a user