mirror of
https://github.com/flavioribeiro/donut.git
synced 2025-09-27 03:15:54 +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
|
ENV GOPROXY=direct
|
||||||
|
|
||||||
COPY . ./donut
|
COPY . ./donut
|
||||||
# TODO: To remove this hack once the upstream gets merged
|
|
||||||
COPY ./local-tmp-go-astiav// /Users/leandro.moreira/src/go-astiav/
|
|
||||||
WORKDIR ${WD}/donut
|
WORKDIR ${WD}/donut
|
||||||
RUN go build .
|
RUN go build .
|
||||||
CMD ["/usr/src/app/donut/donut", "--enable-ice-mux=true"]
|
CMD ["/usr/src/app/donut/donut", "--enable-ice-mux=true"]
|
5
go.mod
generated
5
go.mod
generated
@@ -2,11 +2,8 @@ module github.com/flavioribeiro/donut
|
|||||||
|
|
||||||
go 1.19
|
go 1.19
|
||||||
|
|
||||||
// TODO: To remove this hack once the upstream gets merged
|
|
||||||
replace github.com/asticode/go-astiav => /Users/leandro.moreira/src/go-astiav
|
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/asticode/go-astiav v0.12.0
|
github.com/asticode/go-astiav v0.14.2-0.20240514161420-d8844951c978
|
||||||
github.com/asticode/go-astikit v0.42.0
|
github.com/asticode/go-astikit v0.42.0
|
||||||
github.com/kelseyhightower/envconfig v1.4.0
|
github.com/kelseyhightower/envconfig v1.4.0
|
||||||
github.com/pion/webrtc/v3 v3.1.47
|
github.com/pion/webrtc/v3 v3.1.47
|
||||||
|
2
go.sum
generated
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 h1:pnir/2KLUSr0527Tv908iAH6EGYYrYta132vvjXsH5w=
|
||||||
github.com/asticode/go-astikit v0.42.0/go.mod h1:h4ly7idim1tNhaVkdVBeXQZEE3L0xblP7fCWbgwipF0=
|
github.com/asticode/go-astikit v0.42.0/go.mod h1:h4ly7idim1tNhaVkdVBeXQZEE3L0xblP7fCWbgwipF0=
|
||||||
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
|
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
|
||||||
|
@@ -75,7 +75,7 @@ type streamContext struct {
|
|||||||
encPkt *astiav.Packet
|
encPkt *astiav.Packet
|
||||||
|
|
||||||
// Bit stream filter
|
// Bit stream filter
|
||||||
bsfContext *astiav.BSFContext
|
bsfContext *astiav.BitStreamFilterContext
|
||||||
}
|
}
|
||||||
|
|
||||||
type libAVParams struct {
|
type libAVParams struct {
|
||||||
@@ -540,7 +540,7 @@ func (c *LibAVFFmpegStreamer) prepareBitStreamFilters(p *libAVParams, closer *as
|
|||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
s.bsfContext, err = astiav.AllocBitStreamContext(bsf)
|
s.bsfContext, err = astiav.AllocBitStreamFilterContext(bsf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error while allocating bit stream context %w", err)
|
return fmt.Errorf("error while allocating bit stream context %w", err)
|
||||||
}
|
}
|
||||||
@@ -551,7 +551,7 @@ func (c *LibAVFFmpegStreamer) prepareBitStreamFilters(p *libAVParams, closer *as
|
|||||||
return fmt.Errorf("error while copying codec parameters %w", err)
|
return fmt.Errorf("error while copying codec parameters %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.bsfContext.Init(); err != nil {
|
if err := s.bsfContext.Initialize(); err != nil {
|
||||||
return fmt.Errorf("error while initiating %w", err)
|
return fmt.Errorf("error while initiating %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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