mirror of
https://github.com/asticode/go-astiav.git
synced 2025-10-16 13:10:40 +08:00
Initial commit
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
.DS_STORE
|
||||
cover*
|
||||
tmp
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
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.
|
13
Makefile
Normal file
13
Makefile
Normal file
@@ -0,0 +1,13 @@
|
||||
version = "n4.4.1"
|
||||
srcPath = "tmp/$(version)/src"
|
||||
|
||||
generate-flags:
|
||||
go run internal/cmd/flags/main.go
|
||||
|
||||
install-ffmpeg:
|
||||
mkdir -p $(srcPath)
|
||||
git clone https://github.com/FFmpeg/FFmpeg $(srcPath)
|
||||
cd $(srcPath) && git checkout $(version)
|
||||
cd $(srcPath) && ./configure --prefix=../.. $(configure)
|
||||
cd $(srcPath) && make
|
||||
cd $(srcPath) && make install
|
50
README.md
Normal file
50
README.md
Normal file
@@ -0,0 +1,50 @@
|
||||
`astiav` is a Golang library providing C bindings for [ffmpeg](https://github.com/FFmpeg/FFmpeg)
|
||||
|
||||
It's only compatible with `ffmpeg` `n4.4.1`.
|
||||
|
||||
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/n4.4.1/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/n4.4.1/doc/examples).
|
||||
|
||||
|name|astiav|ffmpeg|
|
||||
|---|---|---|
|
||||
|Demuxing/Decoding|[see](examples/demuxing_decoding/main.go)|[see](https://github.com/FFmpeg/FFmpeg/blob/n4.4.1/doc/examples/demuxing_decoding.c)
|
||||
|Filtering|[see](examples/filtering/main.go)|[see](https://github.com/FFmpeg/FFmpeg/blob/n4.4.1/doc/examples/filtering_video.c)
|
||||
|Remuxing|[see](examples/remuxing/main.go)|[see](https://github.com/FFmpeg/FFmpeg/blob/n4.4.1/doc/examples/remuxing.c)
|
||||
|Transcoding|[see](examples/transcoding/main.go)|[see](https://github.com/FFmpeg/FFmpeg/blob/n4.4.1/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/n4.4.1/lib/",
|
||||
export CGO_CXXFLAGS="-I{{ path to your working directory }}/tmp/n4.4.1/include/",
|
||||
export PKG_CONFIG_PATH="{{ path to your working directory }}/tmp/n4.4.1/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`.
|
31
astiav_test.go
Normal file
31
astiav_test.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package astiav_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/asticode/go-astiav"
|
||||
"github.com/asticode/go-astikit"
|
||||
)
|
||||
|
||||
var global = struct {
|
||||
closer *astikit.Closer
|
||||
frame *astiav.Frame
|
||||
inputFormatContext *astiav.FormatContext
|
||||
inputStream1 *astiav.Stream
|
||||
inputStream2 *astiav.Stream
|
||||
pkt *astiav.Packet
|
||||
}{
|
||||
closer: astikit.NewCloser(),
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
// Run
|
||||
m.Run()
|
||||
|
||||
// Make sure to close closer
|
||||
global.closer.Close()
|
||||
|
||||
// Exit
|
||||
os.Exit(0)
|
||||
}
|
13
buffersink_flag.go
Normal file
13
buffersink_flag.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package astiav
|
||||
|
||||
//#cgo pkg-config: libavfilter
|
||||
//#include <libavfilter/buffersink.h>
|
||||
import "C"
|
||||
|
||||
type BuffersinkFlag int
|
||||
|
||||
// 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)
|
||||
)
|
14
buffersrc_flag.go
Normal file
14
buffersrc_flag.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package astiav
|
||||
|
||||
//#cgo pkg-config: libavfilter
|
||||
//#include <libavfilter/buffersrc.h>
|
||||
import "C"
|
||||
|
||||
type BuffersrcFlag int
|
||||
|
||||
// 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)
|
||||
)
|
37
bytes.go
Normal file
37
bytes.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package astiav
|
||||
|
||||
//#include <stdlib.h>
|
||||
import "C"
|
||||
import (
|
||||
"errors"
|
||||
"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.int) *C.uint8_t) []byte {
|
||||
var size int
|
||||
r := fn((*C.int)(unsafe.Pointer(&size)))
|
||||
return C.GoBytes(unsafe.Pointer(r), C.int(size))
|
||||
}
|
||||
|
||||
func bytesToC(b []byte, fn func(b *C.uint8_t, size C.int) 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.int(len(b)))
|
||||
}
|
9
bytes_test.go
Normal file
9
bytes_test.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package astiav
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestBytes(t *testing.T) {
|
||||
// TODO Test stringFromC
|
||||
// TODO Test bytesFromC
|
||||
// TODO Test bytesToC
|
||||
}
|
55
channel_layout.go
Normal file
55
channel_layout.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package astiav
|
||||
|
||||
//#cgo pkg-config: libavutil
|
||||
//#include <libavutil/channel_layout.h>
|
||||
import "C"
|
||||
|
||||
type ChannelLayout uint64
|
||||
|
||||
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavutil/channel_layout.h#L90
|
||||
const (
|
||||
ChannelLayoutMono = ChannelLayout(C.AV_CH_LAYOUT_MONO)
|
||||
ChannelLayoutStereo = ChannelLayout(C.AV_CH_LAYOUT_STEREO)
|
||||
ChannelLayout2Point1 = ChannelLayout(C.AV_CH_LAYOUT_2POINT1)
|
||||
ChannelLayout21 = ChannelLayout(C.AV_CH_LAYOUT_2_1)
|
||||
ChannelLayoutSurround = ChannelLayout(C.AV_CH_LAYOUT_SURROUND)
|
||||
ChannelLayout3Point1 = ChannelLayout(C.AV_CH_LAYOUT_3POINT1)
|
||||
ChannelLayout4Point0 = ChannelLayout(C.AV_CH_LAYOUT_4POINT0)
|
||||
ChannelLayout4Point1 = ChannelLayout(C.AV_CH_LAYOUT_4POINT1)
|
||||
ChannelLayout22 = ChannelLayout(C.AV_CH_LAYOUT_2_2)
|
||||
ChannelLayoutQuad = ChannelLayout(C.AV_CH_LAYOUT_QUAD)
|
||||
ChannelLayout5Point0 = ChannelLayout(C.AV_CH_LAYOUT_5POINT0)
|
||||
ChannelLayout5Point1 = ChannelLayout(C.AV_CH_LAYOUT_5POINT1)
|
||||
ChannelLayout5Point0Back = ChannelLayout(C.AV_CH_LAYOUT_5POINT0_BACK)
|
||||
ChannelLayout5Point1Back = ChannelLayout(C.AV_CH_LAYOUT_5POINT1_BACK)
|
||||
ChannelLayout6Point0 = ChannelLayout(C.AV_CH_LAYOUT_6POINT0)
|
||||
ChannelLayout6Point0Front = ChannelLayout(C.AV_CH_LAYOUT_6POINT0_FRONT)
|
||||
ChannelLayoutHexagonal = ChannelLayout(C.AV_CH_LAYOUT_HEXAGONAL)
|
||||
ChannelLayout6Point1 = ChannelLayout(C.AV_CH_LAYOUT_6POINT1)
|
||||
ChannelLayout6Point1Back = ChannelLayout(C.AV_CH_LAYOUT_6POINT1_BACK)
|
||||
ChannelLayout6Point1Front = ChannelLayout(C.AV_CH_LAYOUT_6POINT1_FRONT)
|
||||
ChannelLayout7Point0 = ChannelLayout(C.AV_CH_LAYOUT_7POINT0)
|
||||
ChannelLayout7Point0Front = ChannelLayout(C.AV_CH_LAYOUT_7POINT0_FRONT)
|
||||
ChannelLayout7Point1 = ChannelLayout(C.AV_CH_LAYOUT_7POINT1)
|
||||
ChannelLayout7Point1Wide = ChannelLayout(C.AV_CH_LAYOUT_7POINT1_WIDE)
|
||||
ChannelLayout7Point1WideBack = ChannelLayout(C.AV_CH_LAYOUT_7POINT1_WIDE_BACK)
|
||||
ChannelLayoutOctagonal = ChannelLayout(C.AV_CH_LAYOUT_OCTAGONAL)
|
||||
ChannelLayoutHexadecagonal = ChannelLayout(C.AV_CH_LAYOUT_HEXADECAGONAL)
|
||||
ChannelLayoutStereoDownmix = ChannelLayout(C.AV_CH_LAYOUT_STEREO_DOWNMIX)
|
||||
)
|
||||
|
||||
func (l ChannelLayout) NbChannels() int {
|
||||
return int(C.av_get_channel_layout_nb_channels(C.uint64_t(l)))
|
||||
}
|
||||
|
||||
func (l ChannelLayout) String() string {
|
||||
return l.StringWithNbChannels(l.NbChannels())
|
||||
}
|
||||
|
||||
func (l ChannelLayout) StringWithNbChannels(nbChannels int) string {
|
||||
s, _ := stringFromC(255, func(buf *C.char, size C.size_t) error {
|
||||
C.av_get_channel_layout_string(buf, C.int(size), C.int(nbChannels), C.uint64_t(l))
|
||||
return nil
|
||||
})
|
||||
return s
|
||||
}
|
14
channel_layout_test.go
Normal file
14
channel_layout_test.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package astiav_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/asticode/go-astiav"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestChannelLayout(t *testing.T) {
|
||||
require.Equal(t, 2, astiav.ChannelLayoutStereo.NbChannels())
|
||||
require.Equal(t, "stereo", astiav.ChannelLayoutStereo.String())
|
||||
require.Equal(t, "1 channels (FL+FR)", astiav.ChannelLayoutStereo.StringWithNbChannels(1))
|
||||
}
|
19
chroma_location.go
Normal file
19
chroma_location.go
Normal file
@@ -0,0 +1,19 @@
|
||||
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)
|
||||
)
|
101
codec.go
Normal file
101
codec.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package astiav
|
||||
|
||||
//#cgo pkg-config: libavcodec
|
||||
//#include <libavcodec/avcodec.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) ChannelLayouts() (o []ChannelLayout) {
|
||||
if c.c.channel_layouts == nil {
|
||||
return nil
|
||||
}
|
||||
size := unsafe.Sizeof(*c.c.channel_layouts)
|
||||
for i := 0; ; i++ {
|
||||
p := *(*C.int64_t)(unsafe.Pointer(uintptr(unsafe.Pointer(c.c.channel_layouts)) + uintptr(i)*size))
|
||||
if p == 0 {
|
||||
break
|
||||
}
|
||||
o = append(o, ChannelLayout(p))
|
||||
}
|
||||
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))
|
||||
}
|
254
codec_context.go
Normal file
254
codec_context.go
Normal file
@@ -0,0 +1,254 @@
|
||||
package astiav
|
||||
|
||||
//#cgo pkg-config: libavcodec libavutil
|
||||
//#include <libavcodec/avcodec.h>
|
||||
//#include <libavutil/frame.h>
|
||||
import "C"
|
||||
|
||||
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavcodec/avcodec.h#L383
|
||||
type CodecContext struct {
|
||||
c *C.struct_AVCodecContext
|
||||
}
|
||||
|
||||
func AllocCodecContext(c *Codec) *CodecContext {
|
||||
var cc *C.struct_AVCodec
|
||||
if c != nil {
|
||||
cc = c.c
|
||||
}
|
||||
return newCodecContextFromC(C.avcodec_alloc_context3(cc))
|
||||
}
|
||||
|
||||
func newCodecContextFromC(c *C.struct_AVCodecContext) *CodecContext {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return &CodecContext{c: c}
|
||||
}
|
||||
|
||||
func (cc *CodecContext) Free() {
|
||||
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 {
|
||||
return ChannelLayout(cc.c.channel_layout)
|
||||
}
|
||||
|
||||
func (cc *CodecContext) SetChannelLayout(l ChannelLayout) {
|
||||
cc.c.channel_layout = C.uint64_t(l)
|
||||
}
|
||||
|
||||
func (cc *CodecContext) ChromaLocation() ChromaLocation {
|
||||
return ChromaLocation(cc.c.chroma_sample_location)
|
||||
}
|
||||
|
||||
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) 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) 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) 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))
|
||||
}
|
45
codec_context_flag.go
Normal file
45
codec_context_flag.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package astiav
|
||||
|
||||
//#cgo pkg-config: libavcodec
|
||||
//#include <libavcodec/avcodec.h>
|
||||
import "C"
|
||||
|
||||
type CodecContextFlag int
|
||||
|
||||
// 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 int
|
||||
|
||||
// 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)
|
||||
)
|
115
codec_context_test.go
Normal file
115
codec_context_test.go
Normal file
@@ -0,0 +1,115 @@
|
||||
package astiav_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/asticode/go-astiav"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCodecContext(t *testing.T) {
|
||||
_, s1, s2, err := videoInputStreams()
|
||||
require.NoError(t, err)
|
||||
|
||||
c1 := astiav.FindDecoder(s1.CodecParameters().CodecID())
|
||||
require.NotNil(t, c1)
|
||||
cc1 := astiav.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, 320x180 [SAR 1:1 DAR 16:9], 441 kb/s", cc1.String())
|
||||
require.Equal(t, int64(441324), cc1.BitRate())
|
||||
require.Equal(t, astiav.ChromaLocationLeft, cc1.ChromaLocation())
|
||||
require.Equal(t, astiav.CodecIDH264, cc1.CodecID())
|
||||
require.Equal(t, astiav.ColorPrimariesUnspecified, cc1.ColorPrimaries())
|
||||
require.Equal(t, astiav.ColorRangeUnspecified, cc1.ColorRange())
|
||||
require.Equal(t, astiav.ColorSpaceUnspecified, cc1.ColorSpace())
|
||||
require.Equal(t, astiav.ColorTransferCharacteristicUnspecified, cc1.ColorTransferCharacteristic())
|
||||
require.Equal(t, 12, cc1.GopSize())
|
||||
require.Equal(t, 180, cc1.Height())
|
||||
require.Equal(t, astiav.Level(13), cc1.Level())
|
||||
require.Equal(t, astiav.MediaTypeVideo, cc1.MediaType())
|
||||
require.Equal(t, astiav.PixelFormatYuv420P, cc1.PixelFormat())
|
||||
require.Equal(t, astiav.ProfileH264ConstrainedBaseline, cc1.Profile())
|
||||
require.Equal(t, astiav.NewRational(1, 1), cc1.SampleAspectRatio())
|
||||
require.Equal(t, astiav.StrictStdComplianceNormal, cc1.StrictStdCompliance())
|
||||
require.Equal(t, 1, cc1.ThreadCount())
|
||||
require.Equal(t, astiav.ThreadType(3), cc1.ThreadType())
|
||||
require.Equal(t, 320, cc1.Width())
|
||||
|
||||
c2 := astiav.FindDecoder(s2.CodecParameters().CodecID())
|
||||
require.NotNil(t, c2)
|
||||
cc2 := astiav.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.Equal(t, astiav.ChannelLayoutStereo, cc2.ChannelLayout())
|
||||
require.Equal(t, astiav.CodecIDAac, cc2.CodecID())
|
||||
require.Equal(t, 1024, cc2.FrameSize())
|
||||
require.Equal(t, astiav.MediaTypeAudio, cc2.MediaType())
|
||||
require.Equal(t, astiav.SampleFormatFltp, cc2.SampleFormat())
|
||||
require.Equal(t, 48000, cc2.SampleRate())
|
||||
require.Equal(t, astiav.StrictStdComplianceNormal, cc2.StrictStdCompliance())
|
||||
require.Equal(t, 1, cc2.ThreadCount())
|
||||
require.Equal(t, astiav.ThreadType(3), cc2.ThreadType())
|
||||
|
||||
c3 := astiav.FindEncoder(astiav.CodecIDMjpeg)
|
||||
require.NotNil(t, c3)
|
||||
cc3 := astiav.AllocCodecContext(c3)
|
||||
require.NotNil(t, cc3)
|
||||
defer cc3.Free()
|
||||
cc3.SetHeight(2)
|
||||
cc3.SetPixelFormat(astiav.PixelFormatYuvj420P)
|
||||
cc3.SetTimeBase(astiav.NewRational(1, 1))
|
||||
cc3.SetWidth(3)
|
||||
err = cc3.Open(c3, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
cc4 := astiav.AllocCodecContext(nil)
|
||||
require.NotNil(t, cc4)
|
||||
defer cc4.Free()
|
||||
cc4.SetBitRate(1)
|
||||
cc4.SetChannelLayout(astiav.ChannelLayout21)
|
||||
cc4.SetChannels(3)
|
||||
cc4.SetFlags(astiav.NewCodecContextFlags(4))
|
||||
cc4.SetFlags2(astiav.NewCodecContextFlags2(5))
|
||||
cc4.SetFramerate(astiav.NewRational(6, 1))
|
||||
cc4.SetGopSize(7)
|
||||
cc4.SetHeight(8)
|
||||
cc4.SetPixelFormat(astiav.PixelFormat0Bgr)
|
||||
cc4.SetSampleAspectRatio(astiav.NewRational(10, 1))
|
||||
cc4.SetSampleFormat(astiav.SampleFormatDbl)
|
||||
cc4.SetSampleRate(12)
|
||||
cc4.SetStrictStdCompliance(astiav.StrictStdComplianceExperimental)
|
||||
cc4.SetThreadCount(13)
|
||||
cc4.SetThreadType(astiav.ThreadTypeSlice)
|
||||
cc4.SetTimeBase(astiav.NewRational(15, 1))
|
||||
cc4.SetWidth(16)
|
||||
require.Equal(t, int64(1), cc4.BitRate())
|
||||
require.Equal(t, astiav.ChannelLayout21, cc4.ChannelLayout())
|
||||
require.Equal(t, 3, cc4.Channels())
|
||||
require.Equal(t, astiav.NewCodecContextFlags(4), cc4.Flags())
|
||||
require.Equal(t, astiav.NewCodecContextFlags2(5), cc4.Flags2())
|
||||
require.Equal(t, astiav.NewRational(6, 1), cc4.Framerate())
|
||||
require.Equal(t, 7, cc4.GopSize())
|
||||
require.Equal(t, 8, cc4.Height())
|
||||
require.Equal(t, astiav.PixelFormat0Bgr, cc4.PixelFormat())
|
||||
require.Equal(t, astiav.NewRational(10, 1), cc4.SampleAspectRatio())
|
||||
require.Equal(t, astiav.SampleFormatDbl, cc4.SampleFormat())
|
||||
require.Equal(t, 12, cc4.SampleRate())
|
||||
require.Equal(t, astiav.StrictStdComplianceExperimental, cc4.StrictStdCompliance())
|
||||
require.Equal(t, 13, cc4.ThreadCount())
|
||||
require.Equal(t, astiav.ThreadTypeSlice, cc4.ThreadType())
|
||||
require.Equal(t, astiav.NewRational(15, 1), cc4.TimeBase())
|
||||
require.Equal(t, 16, cc4.Width())
|
||||
|
||||
// TODO Test ReceivePacket
|
||||
// TODO Test SendPacket
|
||||
// TODO Test ReceiveFrame
|
||||
// TODO Test SendFrame
|
||||
}
|
429
codec_id.go
Normal file
429
codec_id.go
Normal file
@@ -0,0 +1,429 @@
|
||||
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)
|
||||
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()
|
||||
}
|
14
codec_id_test.go
Normal file
14
codec_id_test.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package astiav_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/asticode/go-astiav"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCodecID(t *testing.T) {
|
||||
require.Equal(t, astiav.MediaTypeVideo, astiav.CodecIDH264.MediaType())
|
||||
require.Equal(t, "h264", astiav.CodecIDH264.Name())
|
||||
require.Equal(t, "h264", astiav.CodecIDH264.String())
|
||||
}
|
121
codec_parameters.go
Normal file
121
codec_parameters.go
Normal file
@@ -0,0 +1,121 @@
|
||||
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 {
|
||||
return ChannelLayout(cp.c.channel_layout)
|
||||
}
|
||||
|
||||
func (cp *CodecParameters) Channels() int {
|
||||
return int(cp.c.channels)
|
||||
}
|
||||
|
||||
func (cp *CodecParameters) CodecID() CodecID {
|
||||
return CodecID(cp.c.codec_id)
|
||||
}
|
||||
|
||||
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) 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) ColorSpace() ColorSpace {
|
||||
return ColorSpace(cp.c.color_space)
|
||||
}
|
||||
|
||||
func (cp *CodecParameters) ColorTransferCharacteristic() ColorTransferCharacteristic {
|
||||
return ColorTransferCharacteristic(cp.c.color_trc)
|
||||
}
|
||||
|
||||
func (cp *CodecParameters) FrameSize() int {
|
||||
return int(cp.c.frame_size)
|
||||
}
|
||||
|
||||
func (cp *CodecParameters) Height() int {
|
||||
return int(cp.c.height)
|
||||
}
|
||||
|
||||
func (cp *CodecParameters) Level() Level {
|
||||
return Level(cp.c.level)
|
||||
}
|
||||
|
||||
func (cp *CodecParameters) MediaType() MediaType {
|
||||
return MediaType(cp.c.codec_type)
|
||||
}
|
||||
|
||||
func (cp *CodecParameters) PixelFormat() PixelFormat {
|
||||
return PixelFormat(cp.c.format)
|
||||
}
|
||||
|
||||
func (cp *CodecParameters) Profile() Profile {
|
||||
return Profile(cp.c.profile)
|
||||
}
|
||||
|
||||
func (cp *CodecParameters) SampleAspectRatio() Rational {
|
||||
return newRationalFromC(cp.c.sample_aspect_ratio)
|
||||
}
|
||||
|
||||
func (cp *CodecParameters) SampleFormat() SampleFormat {
|
||||
return SampleFormat(cp.c.format)
|
||||
}
|
||||
|
||||
func (cp *CodecParameters) SampleRate() int {
|
||||
return int(cp.c.sample_rate)
|
||||
}
|
||||
|
||||
func (cp *CodecParameters) Width() int {
|
||||
return int(cp.c.width)
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
68
codec_parameters_test.go
Normal file
68
codec_parameters_test.go
Normal file
@@ -0,0 +1,68 @@
|
||||
package astiav_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/asticode/go-astiav"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCodecParameters(t *testing.T) {
|
||||
_, s1, s2, err := videoInputStreams()
|
||||
require.NoError(t, err)
|
||||
|
||||
cp1 := s1.CodecParameters()
|
||||
require.Equal(t, int64(441324), cp1.BitRate())
|
||||
require.Equal(t, astiav.ChromaLocationLeft, cp1.ChromaLocation())
|
||||
require.Equal(t, astiav.CodecIDH264, cp1.CodecID())
|
||||
require.Equal(t, astiav.CodecTag(0x31637661), cp1.CodecTag())
|
||||
require.Equal(t, astiav.ColorPrimariesUnspecified, cp1.ColorPrimaries())
|
||||
require.Equal(t, astiav.ColorRangeUnspecified, cp1.ColorRange())
|
||||
require.Equal(t, astiav.ColorSpaceUnspecified, cp1.ColorSpace())
|
||||
require.Equal(t, astiav.ColorTransferCharacteristicUnspecified, cp1.ColorTransferCharacteristic())
|
||||
require.Equal(t, 180, cp1.Height())
|
||||
require.Equal(t, astiav.Level(13), cp1.Level())
|
||||
require.Equal(t, astiav.MediaTypeVideo, cp1.MediaType())
|
||||
require.Equal(t, astiav.PixelFormatYuv420P, cp1.PixelFormat())
|
||||
require.Equal(t, astiav.ProfileH264ConstrainedBaseline, cp1.Profile())
|
||||
require.Equal(t, astiav.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.Equal(t, astiav.ChannelLayoutStereo, cp2.ChannelLayout())
|
||||
require.Equal(t, astiav.CodecIDAac, cp2.CodecID())
|
||||
require.Equal(t, astiav.CodecTag(0x6134706d), cp2.CodecTag())
|
||||
require.Equal(t, 1024, cp2.FrameSize())
|
||||
require.Equal(t, astiav.MediaTypeAudio, cp2.MediaType())
|
||||
require.Equal(t, astiav.SampleFormatFltp, cp2.SampleFormat())
|
||||
require.Equal(t, 48000, cp2.SampleRate())
|
||||
|
||||
cp3 := astiav.AllocCodecParameters()
|
||||
require.NotNil(t, cp3)
|
||||
defer cp3.Free()
|
||||
err = cp2.Copy(cp3)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 2, cp3.Channels())
|
||||
|
||||
cc4 := astiav.AllocCodecContext(nil)
|
||||
require.NotNil(t, cc4)
|
||||
defer cc4.Free()
|
||||
err = cp2.ToCodecContext(cc4)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 2, cc4.Channels())
|
||||
|
||||
cp5 := astiav.AllocCodecParameters()
|
||||
require.NotNil(t, cp5)
|
||||
defer cp5.Free()
|
||||
err = cp5.FromCodecContext(cc4)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 2, cp5.Channels())
|
||||
|
||||
cp6 := astiav.AllocCodecParameters()
|
||||
require.NotNil(t, cp6)
|
||||
defer cp6.Free()
|
||||
cp6.SetCodecTag(astiav.CodecTag(2))
|
||||
require.Equal(t, astiav.CodecTag(2), cp6.CodecTag())
|
||||
}
|
3
codec_tag.go
Normal file
3
codec_tag.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package astiav
|
||||
|
||||
type CodecTag uint32
|
65
codec_test.go
Normal file
65
codec_test.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package astiav_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/asticode/go-astiav"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCodec(t *testing.T) {
|
||||
c := astiav.FindDecoder(astiav.CodecIDMp3)
|
||||
require.NotNil(t, c)
|
||||
require.Nil(t, c.ChannelLayouts())
|
||||
require.True(t, c.IsDecoder())
|
||||
require.False(t, c.IsEncoder())
|
||||
require.Nil(t, c.PixelFormats())
|
||||
require.Equal(t, []astiav.SampleFormat{astiav.SampleFormatFltp, astiav.SampleFormatFlt}, c.SampleFormats())
|
||||
require.Equal(t, "mp3float", c.Name())
|
||||
require.Equal(t, "mp3float", c.String())
|
||||
|
||||
c = astiav.FindDecoderByName("aac")
|
||||
require.NotNil(t, c)
|
||||
require.Equal(t, []astiav.ChannelLayout{
|
||||
astiav.ChannelLayoutMono,
|
||||
astiav.ChannelLayoutStereo,
|
||||
astiav.ChannelLayoutSurround,
|
||||
astiav.ChannelLayout4Point0,
|
||||
astiav.ChannelLayout5Point0Back,
|
||||
astiav.ChannelLayout5Point1Back,
|
||||
astiav.ChannelLayout7Point1WideBack,
|
||||
}, c.ChannelLayouts())
|
||||
require.True(t, c.IsDecoder())
|
||||
require.False(t, c.IsEncoder())
|
||||
require.Equal(t, []astiav.SampleFormat{astiav.SampleFormatFltp}, c.SampleFormats())
|
||||
require.Equal(t, "aac", c.Name())
|
||||
require.Equal(t, "aac", c.String())
|
||||
|
||||
c = astiav.FindEncoder(astiav.CodecIDH264)
|
||||
require.NotNil(t, c)
|
||||
require.False(t, c.IsDecoder())
|
||||
require.True(t, c.IsEncoder())
|
||||
require.Equal(t, []astiav.PixelFormat{
|
||||
astiav.PixelFormatVideotoolbox,
|
||||
astiav.PixelFormatNv12,
|
||||
astiav.PixelFormatYuv420P,
|
||||
}, c.PixelFormats())
|
||||
require.Nil(t, c.SampleFormats())
|
||||
require.Equal(t, "h264_videotoolbox", c.Name())
|
||||
require.Equal(t, "h264_videotoolbox", c.String())
|
||||
|
||||
c = astiav.FindEncoderByName("mjpeg")
|
||||
require.NotNil(t, c)
|
||||
require.False(t, c.IsDecoder())
|
||||
require.True(t, c.IsEncoder())
|
||||
require.Equal(t, []astiav.PixelFormat{
|
||||
astiav.PixelFormatYuvj420P,
|
||||
astiav.PixelFormatYuvj422P,
|
||||
astiav.PixelFormatYuvj444P,
|
||||
}, c.PixelFormats())
|
||||
require.Equal(t, "mjpeg", c.Name())
|
||||
require.Equal(t, "mjpeg", c.String())
|
||||
|
||||
c = astiav.FindDecoderByName("invalid")
|
||||
require.Nil(t, c)
|
||||
}
|
27
color_primaries.go
Normal file
27
color_primaries.go
Normal file
@@ -0,0 +1,27 @@
|
||||
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)
|
||||
)
|
15
color_range.go
Normal file
15
color_range.go
Normal file
@@ -0,0 +1,15 @@
|
||||
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)
|
||||
)
|
28
color_space.go
Normal file
28
color_space.go
Normal file
@@ -0,0 +1,28 @@
|
||||
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)
|
||||
)
|
33
color_transfer_characteristic.go
Normal file
33
color_transfer_characteristic.go
Normal file
@@ -0,0 +1,33 @@
|
||||
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)
|
||||
)
|
73
dictionary.go
Normal file
73
dictionary.go
Normal file
@@ -0,0 +1,73 @@
|
||||
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.int) *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.int) error {
|
||||
return newError(C.av_packet_unpack_dictionary(b, size, &d.c))
|
||||
})
|
||||
}
|
22
dictionary_entry.go
Normal file
22
dictionary_entry.go
Normal file
@@ -0,0 +1,22 @@
|
||||
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)
|
||||
}
|
18
dictionary_flag.go
Normal file
18
dictionary_flag.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package astiav
|
||||
|
||||
//#cgo pkg-config: libavutil
|
||||
//#include <libavutil/dict.h>
|
||||
import "C"
|
||||
|
||||
type DictionaryFlag int
|
||||
|
||||
// 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)
|
||||
)
|
42
dictionary_test.go
Normal file
42
dictionary_test.go
Normal file
@@ -0,0 +1,42 @@
|
||||
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())
|
||||
}
|
68
error.go
Normal file
68
error.go
Normal file
@@ -0,0 +1,68 @@
|
||||
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)
|
||||
}
|
23
error_test.go
Normal file
23
error_test.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package astiav_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/asticode/go-astiav"
|
||||
"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", astiav.ErrDecoderNotFound.Error())
|
||||
err1 := fmt.Errorf("test 1: %w", astiav.ErrDecoderNotFound)
|
||||
require.True(t, errors.Is(err1, astiav.ErrDecoderNotFound))
|
||||
require.False(t, errors.Is(err1, testError{}))
|
||||
err2 := fmt.Errorf("test 2: %w", astiav.ErrDemuxerNotFound)
|
||||
require.False(t, errors.Is(err2, astiav.ErrDecoderNotFound))
|
||||
}
|
140
examples/demuxing_decoding/main.go
Normal file
140
examples/demuxing_decoding/main.go
Normal file
@@ -0,0 +1,140 @@
|
||||
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(l astiav.LogLevel, msg, parent string) {
|
||||
log.Printf("ffmpeg log: %s (level: %d)\n", strings.TrimSpace(msg), 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")
|
||||
}
|
299
examples/filtering/main.go
Normal file
299
examples/filtering/main.go
Normal file
@@ -0,0 +1,299 @@
|
||||
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(l astiav.LogLevel, msg, parent string) {
|
||||
log.Printf("ffmpeg log: %s (level: %d)\n", strings.TrimSpace(msg), 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
|
||||
}
|
159
examples/remuxing/main.go
Normal file
159
examples/remuxing/main.go
Normal file
@@ -0,0 +1,159 @@
|
||||
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(l astiav.LogLevel, msg, parent string) {
|
||||
log.Printf("ffmpeg log: %s (level: %d)\n", strings.TrimSpace(msg), 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) {
|
||||
// Create io context
|
||||
ioContext := astiav.NewIOContext()
|
||||
|
||||
// Open io context
|
||||
if err = ioContext.Open(*output, astiav.NewIOContextFlags(astiav.IOContextFlagWrite)); err != nil {
|
||||
log.Fatal(fmt.Errorf("main: opening io context failed: %w", err))
|
||||
}
|
||||
defer ioContext.Closep() //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")
|
||||
}
|
506
examples/transcoding/main.go
Normal file
506
examples/transcoding/main.go
Normal file
@@ -0,0 +1,506 @@
|
||||
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(l astiav.LogLevel, msg, parent string) {
|
||||
log.Printf("ffmpeg log: %s (level: %d)\n", strings.TrimSpace(msg), 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) {
|
||||
// Create io context
|
||||
ioContext := astiav.NewIOContext()
|
||||
|
||||
// Open io context
|
||||
if err = ioContext.Open(*output, astiav.NewIOContextFlags(astiav.IOContextFlagWrite)); err != nil {
|
||||
err = fmt.Errorf("main: opening io context failed: %w", err)
|
||||
return
|
||||
}
|
||||
c.AddWithError(ioContext.Closep)
|
||||
|
||||
// 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, "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(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
|
||||
}
|
32
filter.go
Normal file
32
filter.go
Normal file
@@ -0,0 +1,32 @@
|
||||
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()
|
||||
}
|
13
filter_command_flag.go
Normal file
13
filter_command_flag.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package astiav
|
||||
|
||||
//#cgo pkg-config: libavfilter
|
||||
//#include <libavfilter/avfilter.h>
|
||||
import "C"
|
||||
|
||||
type FilterCommandFlag int
|
||||
|
||||
// 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)
|
||||
)
|
62
filter_context.go
Normal file
62
filter_context.go
Normal file
@@ -0,0 +1,62 @@
|
||||
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 "unsafe"
|
||||
|
||||
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavfilter/avfilter.h#L67
|
||||
type FilterContext struct {
|
||||
c *C.struct_AVFilterContext
|
||||
}
|
||||
|
||||
func newFilterContext() *FilterContext {
|
||||
return &FilterContext{}
|
||||
}
|
||||
|
||||
func (fc *FilterContext) Free() {
|
||||
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) 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 := (*[maxArraySize](*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 := (*[maxArraySize](*C.struct_AVFilterLink))(unsafe.Pointer(fc.c.outputs))
|
||||
for i := 0; i < fc.NbOutputs(); i++ {
|
||||
ls = append(ls, newFilterLinkFromC(lcs[i]))
|
||||
}
|
||||
return
|
||||
}
|
79
filter_graph.go
Normal file
79
filter_graph.go
Normal file
@@ -0,0 +1,79 @@
|
||||
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
|
||||
}
|
||||
return &FilterGraph{c: c}
|
||||
}
|
||||
|
||||
func AllocFilterGraph() *FilterGraph {
|
||||
return newFilterGraphFromC(C.avfilter_graph_alloc())
|
||||
}
|
||||
|
||||
func (g *FilterGraph) Free() {
|
||||
C.avfilter_graph_free(&g.c)
|
||||
}
|
||||
|
||||
func (g *FilterGraph) String() string {
|
||||
return C.GoString(C.avfilter_graph_dump(g.c, nil))
|
||||
}
|
||||
|
||||
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))
|
||||
fc := newFilterContext()
|
||||
err := newError(C.avfilter_graph_create_filter(&fc.c, f.c, cn, ca, nil, g.c))
|
||||
return fc, err
|
||||
}
|
||||
|
||||
func (g *FilterGraph) Parse(content string, inputs, outputs *FilterInOut) error {
|
||||
cc := C.CString(content)
|
||||
defer C.free(unsafe.Pointer(cc))
|
||||
return newError(C.avfilter_graph_parse_ptr(g.c, cc, &inputs.c, &outputs.c, 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
|
||||
}
|
88
filter_graph_test.go
Normal file
88
filter_graph_test.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package astiav_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/asticode/go-astiav"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestFilterGraph(t *testing.T) {
|
||||
fg := astiav.AllocFilterGraph()
|
||||
defer fg.Free()
|
||||
|
||||
bufferSink := astiav.FindFilterByName("buffersink")
|
||||
require.NotNil(t, bufferSink)
|
||||
|
||||
fcOut, err := fg.NewFilterContext(bufferSink, "filter_out", nil)
|
||||
require.NoError(t, err)
|
||||
defer fcOut.Free()
|
||||
|
||||
inputs := astiav.AllocFilterInOut()
|
||||
defer inputs.Free()
|
||||
inputs.SetName("out")
|
||||
inputs.SetFilterContext(fcOut)
|
||||
inputs.SetPadIdx(0)
|
||||
inputs.SetNext(nil)
|
||||
|
||||
var outputs *astiav.FilterInOut
|
||||
defer func() {
|
||||
if outputs != nil {
|
||||
outputs.Free()
|
||||
}
|
||||
}()
|
||||
var fcIns []*astiav.FilterContext
|
||||
for i := 0; i < 2; i++ {
|
||||
bufferSrc := astiav.FindFilterByName("buffer")
|
||||
require.NotNil(t, bufferSrc)
|
||||
|
||||
fcIn, err := fg.NewFilterContext(bufferSrc, fmt.Sprintf("filter_in_%d", i+1), astiav.FilterArgs{
|
||||
"pix_fmt": strconv.Itoa(int(astiav.PixelFormatYuv420P)),
|
||||
"pixel_aspect": "1/1",
|
||||
"time_base": "1/1000",
|
||||
"video_size": "1x1",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
fcIns = append(fcIns, fcIn)
|
||||
defer fcIn.Free()
|
||||
|
||||
o := astiav.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, astiav.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, astiav.NewRational(1, 1000), fc.Outputs()[0].TimeBase())
|
||||
}
|
||||
|
||||
resp, err := fg.SendCommand("scale", "invalid", "a", astiav.NewFilterCommandFlags())
|
||||
require.Error(t, err)
|
||||
require.Empty(t, resp)
|
||||
resp, err = fg.SendCommand("scale", "width", "4", astiav.NewFilterCommandFlags().Add(astiav.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
|
||||
}
|
45
filter_in_out.go
Normal file
45
filter_in_out.go
Normal file
@@ -0,0 +1,45 @@
|
||||
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
|
||||
}
|
21
filter_link.go
Normal file
21
filter_link.go
Normal file
@@ -0,0 +1,21 @@
|
||||
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)
|
||||
}
|
15
filter_test.go
Normal file
15
filter_test.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package astiav_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/asticode/go-astiav"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestFilter(t *testing.T) {
|
||||
f := astiav.FindFilterByName("format")
|
||||
require.NotNil(t, f)
|
||||
require.Equal(t, "format", f.Name())
|
||||
require.Equal(t, "format", f.String())
|
||||
}
|
9
flag.go
Normal file
9
flag.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package astiav
|
||||
|
||||
type flags int
|
||||
|
||||
func (fs flags) add(f int) int { return int(fs) | f }
|
||||
|
||||
func (fs flags) del(f int) int { return int(fs) &^ f }
|
||||
|
||||
func (fs flags) has(f int) bool { return int(fs)&f > 0 }
|
17
flag_test.go
Normal file
17
flag_test.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package astiav
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestFlag(t *testing.T) {
|
||||
f := flags(2 | 4)
|
||||
r := f.add(1)
|
||||
require.Equal(t, 7, r)
|
||||
r = f.del(2)
|
||||
require.Equal(t, 4, r)
|
||||
require.False(t, f.has(1))
|
||||
require.True(t, f.has(4))
|
||||
}
|
282
flags.go
Normal file
282
flags.go
Normal file
@@ -0,0 +1,282 @@
|
||||
// Code generated by astiav. DO NOT EDIT.
|
||||
package astiav
|
||||
|
||||
type BuffersinkFlags flags
|
||||
|
||||
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(flags(fs).add(int(f)))
|
||||
}
|
||||
|
||||
func (fs BuffersinkFlags) Del(f BuffersinkFlag) BuffersinkFlags {
|
||||
return BuffersinkFlags(flags(fs).del(int(f)))
|
||||
}
|
||||
|
||||
func (fs BuffersinkFlags) Has(f BuffersinkFlag) bool { return flags(fs).has(int(f)) }
|
||||
|
||||
type BuffersrcFlags flags
|
||||
|
||||
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(flags(fs).add(int(f)))
|
||||
}
|
||||
|
||||
func (fs BuffersrcFlags) Del(f BuffersrcFlag) BuffersrcFlags {
|
||||
return BuffersrcFlags(flags(fs).del(int(f)))
|
||||
}
|
||||
|
||||
func (fs BuffersrcFlags) Has(f BuffersrcFlag) bool { return flags(fs).has(int(f)) }
|
||||
|
||||
type CodecContextFlags flags
|
||||
|
||||
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(flags(fs).add(int(f)))
|
||||
}
|
||||
|
||||
func (fs CodecContextFlags) Del(f CodecContextFlag) CodecContextFlags {
|
||||
return CodecContextFlags(flags(fs).del(int(f)))
|
||||
}
|
||||
|
||||
func (fs CodecContextFlags) Has(f CodecContextFlag) bool { return flags(fs).has(int(f)) }
|
||||
|
||||
type CodecContextFlags2 flags
|
||||
|
||||
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(flags(fs).add(int(f)))
|
||||
}
|
||||
|
||||
func (fs CodecContextFlags2) Del(f CodecContextFlag2) CodecContextFlags2 {
|
||||
return CodecContextFlags2(flags(fs).del(int(f)))
|
||||
}
|
||||
|
||||
func (fs CodecContextFlags2) Has(f CodecContextFlag2) bool { return flags(fs).has(int(f)) }
|
||||
|
||||
type DictionaryFlags flags
|
||||
|
||||
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(flags(fs).add(int(f)))
|
||||
}
|
||||
|
||||
func (fs DictionaryFlags) Del(f DictionaryFlag) DictionaryFlags {
|
||||
return DictionaryFlags(flags(fs).del(int(f)))
|
||||
}
|
||||
|
||||
func (fs DictionaryFlags) Has(f DictionaryFlag) bool { return flags(fs).has(int(f)) }
|
||||
|
||||
type FilterCommandFlags flags
|
||||
|
||||
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(flags(fs).add(int(f)))
|
||||
}
|
||||
|
||||
func (fs FilterCommandFlags) Del(f FilterCommandFlag) FilterCommandFlags {
|
||||
return FilterCommandFlags(flags(fs).del(int(f)))
|
||||
}
|
||||
|
||||
func (fs FilterCommandFlags) Has(f FilterCommandFlag) bool { return flags(fs).has(int(f)) }
|
||||
|
||||
type FormatContextCtxFlags flags
|
||||
|
||||
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(flags(fs).add(int(f)))
|
||||
}
|
||||
|
||||
func (fs FormatContextCtxFlags) Del(f FormatContextCtxFlag) FormatContextCtxFlags {
|
||||
return FormatContextCtxFlags(flags(fs).del(int(f)))
|
||||
}
|
||||
|
||||
func (fs FormatContextCtxFlags) Has(f FormatContextCtxFlag) bool { return flags(fs).has(int(f)) }
|
||||
|
||||
type FormatContextFlags flags
|
||||
|
||||
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(flags(fs).add(int(f)))
|
||||
}
|
||||
|
||||
func (fs FormatContextFlags) Del(f FormatContextFlag) FormatContextFlags {
|
||||
return FormatContextFlags(flags(fs).del(int(f)))
|
||||
}
|
||||
|
||||
func (fs FormatContextFlags) Has(f FormatContextFlag) bool { return flags(fs).has(int(f)) }
|
||||
|
||||
type FormatEventFlags flags
|
||||
|
||||
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(flags(fs).add(int(f)))
|
||||
}
|
||||
|
||||
func (fs FormatEventFlags) Del(f FormatEventFlag) FormatEventFlags {
|
||||
return FormatEventFlags(flags(fs).del(int(f)))
|
||||
}
|
||||
|
||||
func (fs FormatEventFlags) Has(f FormatEventFlag) bool { return flags(fs).has(int(f)) }
|
||||
|
||||
type IOContextFlags flags
|
||||
|
||||
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(flags(fs).add(int(f)))
|
||||
}
|
||||
|
||||
func (fs IOContextFlags) Del(f IOContextFlag) IOContextFlags {
|
||||
return IOContextFlags(flags(fs).del(int(f)))
|
||||
}
|
||||
|
||||
func (fs IOContextFlags) Has(f IOContextFlag) bool { return flags(fs).has(int(f)) }
|
||||
|
||||
type IOFormatFlags flags
|
||||
|
||||
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(flags(fs).add(int(f)))
|
||||
}
|
||||
|
||||
func (fs IOFormatFlags) Del(f IOFormatFlag) IOFormatFlags {
|
||||
return IOFormatFlags(flags(fs).del(int(f)))
|
||||
}
|
||||
|
||||
func (fs IOFormatFlags) Has(f IOFormatFlag) bool { return flags(fs).has(int(f)) }
|
||||
|
||||
type PacketFlags flags
|
||||
|
||||
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(flags(fs).add(int(f)))
|
||||
}
|
||||
|
||||
func (fs PacketFlags) Del(f PacketFlag) PacketFlags {
|
||||
return PacketFlags(flags(fs).del(int(f)))
|
||||
}
|
||||
|
||||
func (fs PacketFlags) Has(f PacketFlag) bool { return flags(fs).has(int(f)) }
|
||||
|
||||
type SeekFlags flags
|
||||
|
||||
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(flags(fs).add(int(f)))
|
||||
}
|
||||
|
||||
func (fs SeekFlags) Del(f SeekFlag) SeekFlags {
|
||||
return SeekFlags(flags(fs).del(int(f)))
|
||||
}
|
||||
|
||||
func (fs SeekFlags) Has(f SeekFlag) bool { return flags(fs).has(int(f)) }
|
||||
|
||||
type StreamEventFlags flags
|
||||
|
||||
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(flags(fs).add(int(f)))
|
||||
}
|
||||
|
||||
func (fs StreamEventFlags) Del(f StreamEventFlag) StreamEventFlags {
|
||||
return StreamEventFlags(flags(fs).del(int(f)))
|
||||
}
|
||||
|
||||
func (fs StreamEventFlags) Has(f StreamEventFlag) bool { return flags(fs).has(int(f)) }
|
134
flags_test.go
Normal file
134
flags_test.go
Normal file
@@ -0,0 +1,134 @@
|
||||
// Code generated by astiav. DO NOT EDIT.
|
||||
package astiav_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/asticode/go-astiav"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestBuffersinkFlags(t *testing.T) {
|
||||
fs := astiav.NewBuffersinkFlags(astiav.BuffersinkFlag(1))
|
||||
require.True(t, fs.Has(astiav.BuffersinkFlag(1)))
|
||||
fs = fs.Add(astiav.BuffersinkFlag(2))
|
||||
require.True(t, fs.Has(astiav.BuffersinkFlag(2)))
|
||||
fs = fs.Del(astiav.BuffersinkFlag(2))
|
||||
require.False(t, fs.Has(astiav.BuffersinkFlag(2)))
|
||||
}
|
||||
|
||||
func TestBuffersrcFlags(t *testing.T) {
|
||||
fs := astiav.NewBuffersrcFlags(astiav.BuffersrcFlag(1))
|
||||
require.True(t, fs.Has(astiav.BuffersrcFlag(1)))
|
||||
fs = fs.Add(astiav.BuffersrcFlag(2))
|
||||
require.True(t, fs.Has(astiav.BuffersrcFlag(2)))
|
||||
fs = fs.Del(astiav.BuffersrcFlag(2))
|
||||
require.False(t, fs.Has(astiav.BuffersrcFlag(2)))
|
||||
}
|
||||
|
||||
func TestCodecContextFlags(t *testing.T) {
|
||||
fs := astiav.NewCodecContextFlags(astiav.CodecContextFlag(1))
|
||||
require.True(t, fs.Has(astiav.CodecContextFlag(1)))
|
||||
fs = fs.Add(astiav.CodecContextFlag(2))
|
||||
require.True(t, fs.Has(astiav.CodecContextFlag(2)))
|
||||
fs = fs.Del(astiav.CodecContextFlag(2))
|
||||
require.False(t, fs.Has(astiav.CodecContextFlag(2)))
|
||||
}
|
||||
|
||||
func TestCodecContextFlags2(t *testing.T) {
|
||||
fs := astiav.NewCodecContextFlags2(astiav.CodecContextFlag2(1))
|
||||
require.True(t, fs.Has(astiav.CodecContextFlag2(1)))
|
||||
fs = fs.Add(astiav.CodecContextFlag2(2))
|
||||
require.True(t, fs.Has(astiav.CodecContextFlag2(2)))
|
||||
fs = fs.Del(astiav.CodecContextFlag2(2))
|
||||
require.False(t, fs.Has(astiav.CodecContextFlag2(2)))
|
||||
}
|
||||
|
||||
func TestDictionaryFlags(t *testing.T) {
|
||||
fs := astiav.NewDictionaryFlags(astiav.DictionaryFlag(1))
|
||||
require.True(t, fs.Has(astiav.DictionaryFlag(1)))
|
||||
fs = fs.Add(astiav.DictionaryFlag(2))
|
||||
require.True(t, fs.Has(astiav.DictionaryFlag(2)))
|
||||
fs = fs.Del(astiav.DictionaryFlag(2))
|
||||
require.False(t, fs.Has(astiav.DictionaryFlag(2)))
|
||||
}
|
||||
|
||||
func TestFilterCommandFlags(t *testing.T) {
|
||||
fs := astiav.NewFilterCommandFlags(astiav.FilterCommandFlag(1))
|
||||
require.True(t, fs.Has(astiav.FilterCommandFlag(1)))
|
||||
fs = fs.Add(astiav.FilterCommandFlag(2))
|
||||
require.True(t, fs.Has(astiav.FilterCommandFlag(2)))
|
||||
fs = fs.Del(astiav.FilterCommandFlag(2))
|
||||
require.False(t, fs.Has(astiav.FilterCommandFlag(2)))
|
||||
}
|
||||
|
||||
func TestFormatContextCtxFlags(t *testing.T) {
|
||||
fs := astiav.NewFormatContextCtxFlags(astiav.FormatContextCtxFlag(1))
|
||||
require.True(t, fs.Has(astiav.FormatContextCtxFlag(1)))
|
||||
fs = fs.Add(astiav.FormatContextCtxFlag(2))
|
||||
require.True(t, fs.Has(astiav.FormatContextCtxFlag(2)))
|
||||
fs = fs.Del(astiav.FormatContextCtxFlag(2))
|
||||
require.False(t, fs.Has(astiav.FormatContextCtxFlag(2)))
|
||||
}
|
||||
|
||||
func TestFormatContextFlags(t *testing.T) {
|
||||
fs := astiav.NewFormatContextFlags(astiav.FormatContextFlag(1))
|
||||
require.True(t, fs.Has(astiav.FormatContextFlag(1)))
|
||||
fs = fs.Add(astiav.FormatContextFlag(2))
|
||||
require.True(t, fs.Has(astiav.FormatContextFlag(2)))
|
||||
fs = fs.Del(astiav.FormatContextFlag(2))
|
||||
require.False(t, fs.Has(astiav.FormatContextFlag(2)))
|
||||
}
|
||||
|
||||
func TestFormatEventFlags(t *testing.T) {
|
||||
fs := astiav.NewFormatEventFlags(astiav.FormatEventFlag(1))
|
||||
require.True(t, fs.Has(astiav.FormatEventFlag(1)))
|
||||
fs = fs.Add(astiav.FormatEventFlag(2))
|
||||
require.True(t, fs.Has(astiav.FormatEventFlag(2)))
|
||||
fs = fs.Del(astiav.FormatEventFlag(2))
|
||||
require.False(t, fs.Has(astiav.FormatEventFlag(2)))
|
||||
}
|
||||
|
||||
func TestIOContextFlags(t *testing.T) {
|
||||
fs := astiav.NewIOContextFlags(astiav.IOContextFlag(1))
|
||||
require.True(t, fs.Has(astiav.IOContextFlag(1)))
|
||||
fs = fs.Add(astiav.IOContextFlag(2))
|
||||
require.True(t, fs.Has(astiav.IOContextFlag(2)))
|
||||
fs = fs.Del(astiav.IOContextFlag(2))
|
||||
require.False(t, fs.Has(astiav.IOContextFlag(2)))
|
||||
}
|
||||
|
||||
func TestIOFormatFlags(t *testing.T) {
|
||||
fs := astiav.NewIOFormatFlags(astiav.IOFormatFlag(1))
|
||||
require.True(t, fs.Has(astiav.IOFormatFlag(1)))
|
||||
fs = fs.Add(astiav.IOFormatFlag(2))
|
||||
require.True(t, fs.Has(astiav.IOFormatFlag(2)))
|
||||
fs = fs.Del(astiav.IOFormatFlag(2))
|
||||
require.False(t, fs.Has(astiav.IOFormatFlag(2)))
|
||||
}
|
||||
|
||||
func TestPacketFlags(t *testing.T) {
|
||||
fs := astiav.NewPacketFlags(astiav.PacketFlag(1))
|
||||
require.True(t, fs.Has(astiav.PacketFlag(1)))
|
||||
fs = fs.Add(astiav.PacketFlag(2))
|
||||
require.True(t, fs.Has(astiav.PacketFlag(2)))
|
||||
fs = fs.Del(astiav.PacketFlag(2))
|
||||
require.False(t, fs.Has(astiav.PacketFlag(2)))
|
||||
}
|
||||
|
||||
func TestSeekFlags(t *testing.T) {
|
||||
fs := astiav.NewSeekFlags(astiav.SeekFlag(1))
|
||||
require.True(t, fs.Has(astiav.SeekFlag(1)))
|
||||
fs = fs.Add(astiav.SeekFlag(2))
|
||||
require.True(t, fs.Has(astiav.SeekFlag(2)))
|
||||
fs = fs.Del(astiav.SeekFlag(2))
|
||||
require.False(t, fs.Has(astiav.SeekFlag(2)))
|
||||
}
|
||||
|
||||
func TestStreamEventFlags(t *testing.T) {
|
||||
fs := astiav.NewStreamEventFlags(astiav.StreamEventFlag(1))
|
||||
require.True(t, fs.Has(astiav.StreamEventFlag(1)))
|
||||
fs = fs.Add(astiav.StreamEventFlag(2))
|
||||
require.True(t, fs.Has(astiav.StreamEventFlag(2)))
|
||||
fs = fs.Del(astiav.StreamEventFlag(2))
|
||||
require.False(t, fs.Has(astiav.StreamEventFlag(2)))
|
||||
}
|
258
format_context.go
Normal file
258
format_context.go
Normal file
@@ -0,0 +1,258 @@
|
||||
package astiav
|
||||
|
||||
//#cgo pkg-config: libavcodec libavformat
|
||||
//#include <libavcodec/avcodec.h>
|
||||
//#include <libavformat/avformat.h>
|
||||
/*
|
||||
int astiavInterruptCallback(void *ret)
|
||||
{
|
||||
return *((int*)ret);
|
||||
}
|
||||
AVIOInterruptCB astiavNewInterruptCallback(int *ret)
|
||||
{
|
||||
AVIOInterruptCB c = { astiavInterruptCallback, ret };
|
||||
return c;
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
maxArraySize = 1<<29 - 1
|
||||
)
|
||||
|
||||
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavformat/avformat.h#L1202
|
||||
type FormatContext struct {
|
||||
c *C.struct_AVFormatContext
|
||||
}
|
||||
|
||||
func newFormatContext() *FormatContext {
|
||||
return &FormatContext{}
|
||||
}
|
||||
|
||||
func newFormatContextFromC(c *C.struct_AVFormatContext) *FormatContext {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return &FormatContext{c: c}
|
||||
}
|
||||
|
||||
func AllocFormatContext() *FormatContext {
|
||||
return newFormatContextFromC(C.avformat_alloc_context())
|
||||
}
|
||||
|
||||
func AllocOutputFormatContext(o *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))
|
||||
}
|
||||
fc := newFormatContext()
|
||||
var oc *C.struct_AVOutputFormat
|
||||
if o != nil {
|
||||
oc = o.c
|
||||
}
|
||||
err := newError(C.avformat_alloc_output_context2(&fc.c, oc, fonc, finc))
|
||||
return fc, err
|
||||
}
|
||||
|
||||
func (fc *FormatContext) BitRate() int64 {
|
||||
return int64(fc.c.bit_rate)
|
||||
}
|
||||
|
||||
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) Filename() string {
|
||||
return C.GoString(&fc.c.filename[0])
|
||||
}
|
||||
|
||||
func (fc *FormatContext) Flags() FormatContextFlags {
|
||||
return FormatContextFlags(fc.c.flags)
|
||||
}
|
||||
|
||||
func (fc *FormatContext) SetInterruptCallback() *int {
|
||||
ret := 0
|
||||
fc.c.interrupt_callback = C.astiavNewInterruptCallback((*C.int)(unsafe.Pointer(&ret)))
|
||||
return &ret
|
||||
}
|
||||
|
||||
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 {
|
||||
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 := (*[maxArraySize](*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
|
||||
}
|
||||
return newError(C.avformat_open_input(&fc.c, urlc, fmtc, dc))
|
||||
}
|
||||
|
||||
func (fc *FormatContext) CloseInput() {
|
||||
C.avformat_close_input(&fc.c)
|
||||
}
|
||||
|
||||
func (fc *FormatContext) Free() {
|
||||
C.avformat_free_context(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)))
|
||||
})
|
||||
}
|
13
format_context_ctx_flag.go
Normal file
13
format_context_ctx_flag.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package astiav
|
||||
|
||||
//#cgo pkg-config: libavformat
|
||||
//#include <libavformat/avformat.h>
|
||||
import "C"
|
||||
|
||||
type FormatContextCtxFlag int
|
||||
|
||||
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavformat/avformat.h#L1153
|
||||
const (
|
||||
FormatContextCtxFlagNoHeader = FormatContextCtxFlag(C.AVFMTCTX_NOHEADER)
|
||||
FormatContextCtxFlagUnseekable = FormatContextCtxFlag(C.AVFMTCTX_UNSEEKABLE)
|
||||
)
|
29
format_context_flag.go
Normal file
29
format_context_flag.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package astiav
|
||||
|
||||
//#cgo pkg-config: libavformat
|
||||
//#include <libavformat/avformat.h>
|
||||
import "C"
|
||||
|
||||
type FormatContextFlag int
|
||||
|
||||
// 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)
|
||||
FormatContextFlagMp4ALatm = FormatContextFlag(C.AVFMT_FLAG_MP4A_LATM)
|
||||
FormatContextFlagSortDts = FormatContextFlag(C.AVFMT_FLAG_SORT_DTS)
|
||||
FormatContextFlagPrivOpt = FormatContextFlag(C.AVFMT_FLAG_PRIV_OPT)
|
||||
FormatContextFlagKeepSideData = FormatContextFlag(C.AVFMT_FLAG_KEEP_SIDE_DATA)
|
||||
FormatContextFlagFastSeek = FormatContextFlag(C.AVFMT_FLAG_FAST_SEEK)
|
||||
FormatContextFlagShortest = FormatContextFlag(C.AVFMT_FLAG_SHORTEST)
|
||||
FormatContextFlagAutoBsf = FormatContextFlag(C.AVFMT_FLAG_AUTO_BSF)
|
||||
)
|
93
format_context_test.go
Normal file
93
format_context_test.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package astiav_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/asticode/go-astiav"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func videoInputFormatContext() (fc1 *astiav.FormatContext, err error) {
|
||||
if global.inputFormatContext != nil {
|
||||
return global.inputFormatContext, nil
|
||||
}
|
||||
|
||||
if fc1 = astiav.AllocFormatContext(); fc1 == nil {
|
||||
err = errors.New("astiav_test: allocated format context is nil")
|
||||
return
|
||||
}
|
||||
global.closer.Add(fc1.Free)
|
||||
|
||||
if err = fc1.OpenInput("testdata/video.mp4", nil, nil); err != nil {
|
||||
err = fmt.Errorf("astiav_test: opening input failed: %w", err)
|
||||
return
|
||||
}
|
||||
global.closer.Add(fc1.CloseInput)
|
||||
|
||||
if err = fc1.FindStreamInfo(nil); err != nil {
|
||||
err = fmt.Errorf("astiav_test: finding stream info failed: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
global.inputFormatContext = fc1
|
||||
return
|
||||
}
|
||||
|
||||
func TestFormatContext(t *testing.T) {
|
||||
fc1, s1, _, err := videoInputStreams()
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, int64(607583), fc1.BitRate())
|
||||
require.Equal(t, astiav.NewFormatContextCtxFlags(0), fc1.CtxFlags())
|
||||
require.Equal(t, int64(5014000), fc1.Duration())
|
||||
require.True(t, fc1.EventFlags().Has(astiav.FormatEventFlagMetadataUpdated))
|
||||
require.Equal(t, "testdata/video.mp4", fc1.Filename())
|
||||
require.True(t, fc1.Flags().Has(astiav.FormatContextFlagAutoBsf))
|
||||
require.Equal(t, astiav.NewRational(24, 1), fc1.GuessFrameRate(s1, nil))
|
||||
require.Equal(t, astiav.NewRational(1, 1), fc1.GuessSampleAspectRatio(s1, nil))
|
||||
require.True(t, fc1.InputFormat().Flags().Has(astiav.IOFormatFlagNoByteSeek))
|
||||
require.Equal(t, astiav.IOContextFlags(0), fc1.IOFlags())
|
||||
require.Equal(t, int64(0), fc1.MaxAnalyzeDuration())
|
||||
require.Equal(t, "isom", fc1.Metadata().Get("major_brand", nil, astiav.NewDictionaryFlags()).Value())
|
||||
require.Equal(t, int64(0), fc1.StartTime())
|
||||
require.Equal(t, 2, fc1.NbStreams())
|
||||
require.Len(t, fc1.Streams(), 2)
|
||||
|
||||
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 58.76.100\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 := astiav.AllocOutputFormatContext(nil, "", "/tmp/test.mp4")
|
||||
require.NoError(t, err)
|
||||
defer fc2.Free()
|
||||
require.Equal(t, "/tmp/test.mp4", fc2.Filename())
|
||||
require.True(t, fc2.OutputFormat().Flags().Has(astiav.IOFormatFlagGlobalheader))
|
||||
|
||||
fc3 := astiav.AllocFormatContext()
|
||||
require.NotNil(t, fc3)
|
||||
defer fc3.Free()
|
||||
c := astiav.NewIOContext()
|
||||
err = c.Open("testdata/video.mp4", astiav.NewIOContextFlags(astiav.IOContextFlagRead))
|
||||
require.NoError(t, err)
|
||||
defer c.Closep() //nolint:errcheck
|
||||
fc3.SetPb(c)
|
||||
fc3.SetStrictStdCompliance(astiav.StrictStdComplianceExperimental)
|
||||
require.NotNil(t, fc3.Pb())
|
||||
require.Equal(t, astiav.StrictStdComplianceExperimental, fc3.StrictStdCompliance())
|
||||
s2 := fc3.NewStream(nil)
|
||||
require.NotNil(t, s2)
|
||||
s3 := fc3.NewStream(nil)
|
||||
require.NotNil(t, s3)
|
||||
require.Equal(t, 1, s3.Index())
|
||||
|
||||
// TODO Test SetInterruptCallback
|
||||
// TODO Test ReadFrame
|
||||
// TODO Test SeekFrame
|
||||
// TODO Test Flush
|
||||
// TODO Test WriteHeader
|
||||
// TODO Test WriteFrame
|
||||
// TODO Test WriteInterleavedFrame
|
||||
// TODO Test WriteTrailer
|
||||
}
|
12
format_event_flag.go
Normal file
12
format_event_flag.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package astiav
|
||||
|
||||
//#cgo pkg-config: libavformat
|
||||
//#include <libavformat/avformat.h>
|
||||
import "C"
|
||||
|
||||
type FormatEventFlag int
|
||||
|
||||
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavformat/avformat.h#L1519
|
||||
const (
|
||||
FormatEventFlagMetadataUpdated = FormatEventFlag(C.AVFMT_EVENT_FLAG_METADATA_UPDATED)
|
||||
)
|
158
frame.go
Normal file
158
frame.go
Normal file
@@ -0,0 +1,158 @@
|
||||
package astiav
|
||||
|
||||
//#cgo pkg-config: libavutil
|
||||
//#include <libavutil/frame.h>
|
||||
import "C"
|
||||
|
||||
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) ChannelLayout() ChannelLayout {
|
||||
return ChannelLayout(f.c.channel_layout)
|
||||
}
|
||||
|
||||
func (f *Frame) SetChannelLayout(l ChannelLayout) {
|
||||
f.c.channel_layout = C.uint64_t(l)
|
||||
}
|
||||
|
||||
func (f *Frame) Data() [NumDataPointers][]byte {
|
||||
b := [NumDataPointers][]byte{}
|
||||
for i := 0; i < int(NumDataPointers); i++ {
|
||||
b[i] = bytesFromC(func(size *C.int) *C.uint8_t {
|
||||
*size = f.c.linesize[i] * f.c.height
|
||||
return f.c.data[i]
|
||||
})
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
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) 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) PktPts() int64 {
|
||||
return int64(f.c.pkt_pts)
|
||||
}
|
||||
|
||||
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) 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) Width() int {
|
||||
return int(f.c.width)
|
||||
}
|
||||
|
||||
func (f *Frame) SetWidth(w int) {
|
||||
f.c.width = C.int(w)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
178
frame_test.go
Normal file
178
frame_test.go
Normal file
@@ -0,0 +1,178 @@
|
||||
package astiav_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/asticode/go-astiav"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func videoInputLastVideoFrame() (f *astiav.Frame, err error) {
|
||||
if global.frame != nil {
|
||||
return global.frame, nil
|
||||
}
|
||||
|
||||
var fc *astiav.FormatContext
|
||||
if fc, err = videoInputFormatContext(); err != nil {
|
||||
err = fmt.Errorf("astiav_test: getting input format context failed: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
var cc *astiav.CodecContext
|
||||
var cs *astiav.Stream
|
||||
for _, s := range fc.Streams() {
|
||||
if s.CodecParameters().MediaType() != astiav.MediaTypeVideo {
|
||||
continue
|
||||
}
|
||||
|
||||
cs = s
|
||||
|
||||
c := astiav.FindDecoder(s.CodecParameters().CodecID())
|
||||
if c == nil {
|
||||
err = errors.New("astiav_test: no codec")
|
||||
return
|
||||
}
|
||||
|
||||
cc = astiav.AllocCodecContext(c)
|
||||
if cc == nil {
|
||||
err = errors.New("astiav_test: no codec context")
|
||||
return
|
||||
}
|
||||
global.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 *astiav.Packet
|
||||
if pkt1, err = videoInputFirstPacket(); err != nil {
|
||||
err = fmt.Errorf("astiav_test: getting input first packet failed: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
pkt2 := astiav.AllocPacket()
|
||||
global.closer.Add(pkt2.Free)
|
||||
|
||||
f = astiav.AllocFrame()
|
||||
global.closer.Add(f.Free)
|
||||
|
||||
lastFrame := astiav.AllocFrame()
|
||||
global.closer.Add(lastFrame.Free)
|
||||
|
||||
pkts := []*astiav.Packet{pkt1}
|
||||
for {
|
||||
if err = fc.ReadFrame(pkt2); err != nil {
|
||||
if errors.Is(err, astiav.ErrEof) || errors.Is(err, astiav.ErrEagain) {
|
||||
if err = f.Ref(lastFrame); err != nil {
|
||||
err = fmt.Errorf("astiav_test: refing frame failed: %w", err)
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
break
|
||||
}
|
||||
err = fmt.Errorf("astiav_test: reading frame failed: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
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, astiav.ErrEof) || errors.Is(err, astiav.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 = []*astiav.Packet{}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func TestFrame(t *testing.T) {
|
||||
f1, err := videoInputLastVideoFrame()
|
||||
require.NoError(t, err)
|
||||
b, err := ioutil.ReadFile("testdata/frame")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, string(b), fmt.Sprintf("%+v", f1.Data()))
|
||||
require.Equal(t, [8]int{384, 192, 192, 0, 0, 0, 0, 0}, f1.Linesize())
|
||||
require.Equal(t, int64(60928), f1.PktDts())
|
||||
require.Equal(t, int64(60928), f1.PktPts())
|
||||
|
||||
f2 := astiav.AllocFrame()
|
||||
require.NotNil(t, f2)
|
||||
defer f2.Free()
|
||||
f2.SetChannelLayout(astiav.ChannelLayout21)
|
||||
f2.SetHeight(2)
|
||||
f2.SetKeyFrame(true)
|
||||
f2.SetNbSamples(4)
|
||||
f2.SetPictureType(astiav.PictureTypeB)
|
||||
f2.SetPixelFormat(astiav.PixelFormat0Bgr)
|
||||
require.Equal(t, astiav.PixelFormat0Bgr, f2.PixelFormat()) // Need to test it right away as sample format actually updates the same field
|
||||
f2.SetPts(7)
|
||||
f2.SetSampleFormat(astiav.SampleFormatDbl)
|
||||
require.Equal(t, astiav.SampleFormatDbl, f2.SampleFormat())
|
||||
f2.SetSampleRate(9)
|
||||
f2.SetWidth(10)
|
||||
require.Equal(t, astiav.ChannelLayout21, f2.ChannelLayout())
|
||||
require.Equal(t, 2, f2.Height())
|
||||
require.True(t, f2.KeyFrame())
|
||||
require.Equal(t, 4, f2.NbSamples())
|
||||
require.Equal(t, astiav.PictureTypeB, f2.PictureType())
|
||||
require.Equal(t, int64(7), f2.Pts())
|
||||
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())
|
||||
}
|
11
go.mod
Normal file
11
go.mod
Normal file
@@ -0,0 +1,11 @@
|
||||
module github.com/asticode/go-astiav
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/asticode/go-astikit v0.28.2
|
||||
github.com/davecgh/go-spew v1.1.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/stretchr/testify v1.7.0
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
|
||||
)
|
13
go.sum
Normal file
13
go.sum
Normal file
@@ -0,0 +1,13 @@
|
||||
github.com/asticode/go-astikit v0.28.2 h1:c2shjqarbZwcQGQ7GPfchG2sSOL/7NHGbdgHTx43RH8=
|
||||
github.com/asticode/go-astikit v0.28.2/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=
|
21
input_format.go
Normal file
21
input_format.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package astiav
|
||||
|
||||
//#cgo pkg-config: libavformat
|
||||
//#include <libavformat/avformat.h>
|
||||
import "C"
|
||||
|
||||
// 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 (f *InputFormat) Flags() IOFormatFlags {
|
||||
return IOFormatFlags(f.c.flags)
|
||||
}
|
28
int_read_write.go
Normal file
28
int_read_write.go
Normal file
@@ -0,0 +1,28 @@
|
||||
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)))
|
||||
}
|
16
int_read_write_test.go
Normal file
16
int_read_write_test.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package astiav_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/asticode/go-astiav"
|
||||
"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), astiav.RL32([]byte{}))
|
||||
require.Equal(t, uint32(0x4030201), astiav.RL32(is))
|
||||
require.Equal(t, uint32(0), astiav.RL32WithOffset([]byte{}, 4))
|
||||
require.Equal(t, uint32(0x8070605), astiav.RL32WithOffset(is, 4))
|
||||
}
|
101
internal/cmd/flags/main.go
Normal file
101
internal/cmd/flags/main.go
Normal file
@@ -0,0 +1,101 @@
|
||||
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: "Dictionary"},
|
||||
{Name: "FilterCommand"},
|
||||
{Name: "FormatContextCtx"},
|
||||
{Name: "FormatContext"},
|
||||
{Name: "FormatEvent"},
|
||||
{Name: "IOContext"},
|
||||
{Name: "IOFormat"},
|
||||
{Name: "Packet"},
|
||||
{Name: "Seek"},
|
||||
{Name: "StreamEvent"},
|
||||
}
|
||||
|
||||
var tmpl = `// Code generated by astiav. DO NOT EDIT.
|
||||
package astiav
|
||||
{{ range $val := . }}
|
||||
type {{ $val.Name }}Flags{{ $val.Suffix }} flags
|
||||
|
||||
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 }}(flags(fs).add(int(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 }}(flags(fs).del(int(f)))
|
||||
}
|
||||
|
||||
func (fs {{ $val.Name }}Flags{{ $val.Suffix }}) Has(f {{ $val.Name }}Flag{{ $val.Suffix }}) bool { return flags(fs).has(int(f)) }
|
||||
{{ end }}`
|
||||
|
||||
var tmplTest = `// Code generated by astiav. DO NOT EDIT.
|
||||
package astiav_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/asticode/go-astiav"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
{{ range $val := . }}
|
||||
func Test{{ $val.Name }}Flags{{ $val.Suffix }}(t *testing.T) {
|
||||
fs := astiav.New{{ $val.Name }}Flags{{ $val.Suffix }}(astiav.{{ $val.Name }}Flag{{ $val.Suffix }}(1))
|
||||
require.True(t, fs.Has(astiav.{{ $val.Name }}Flag{{ $val.Suffix }}(1)))
|
||||
fs = fs.Add(astiav.{{ $val.Name }}Flag{{ $val.Suffix }}(2))
|
||||
require.True(t, fs.Has(astiav.{{ $val.Name }}Flag{{ $val.Suffix }}(2)))
|
||||
fs = fs.Del(astiav.{{ $val.Name }}Flag{{ $val.Suffix }}(2))
|
||||
require.False(t, fs.Has(astiav.{{ $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))
|
||||
}
|
||||
}
|
39
io_context.go
Normal file
39
io_context.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package astiav
|
||||
|
||||
//#cgo pkg-config: libavformat
|
||||
//#include <libavformat/avformat.h>
|
||||
import "C"
|
||||
import "unsafe"
|
||||
|
||||
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavformat/avio.h#L161
|
||||
type IOContext struct {
|
||||
c *C.struct_AVIOContext
|
||||
}
|
||||
|
||||
func NewIOContext() *IOContext {
|
||||
return &IOContext{}
|
||||
}
|
||||
|
||||
func newIOContextFromC(c *C.struct_AVIOContext) *IOContext {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return &IOContext{c: c}
|
||||
}
|
||||
|
||||
func (ic *IOContext) Closep() error {
|
||||
return newError(C.avio_closep(&ic.c))
|
||||
}
|
||||
|
||||
func (ic *IOContext) Open(filename string, flags IOContextFlags) error {
|
||||
cfi := C.CString(filename)
|
||||
defer C.free(unsafe.Pointer(cfi))
|
||||
return newError(C.avio_open(&ic.c, cfi, C.int(flags)))
|
||||
}
|
||||
|
||||
func (ic *IOContext) Write(b []byte) {
|
||||
if b == nil {
|
||||
return
|
||||
}
|
||||
C.avio_write(ic.c, (*C.uchar)(unsafe.Pointer(&b[0])), C.int(len(b)))
|
||||
}
|
16
io_context_flag.go
Normal file
16
io_context_flag.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package astiav
|
||||
|
||||
//#cgo pkg-config: libavformat
|
||||
//#include <libavformat/avformat.h>
|
||||
import "C"
|
||||
|
||||
type IOContextFlag int
|
||||
|
||||
// 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)
|
||||
)
|
27
io_context_test.go
Normal file
27
io_context_test.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package astiav_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/asticode/go-astiav"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestIOContext(t *testing.T) {
|
||||
c := astiav.NewIOContext()
|
||||
path := filepath.Join(t.TempDir(), "iocontext.txt")
|
||||
err := c.Open(path, astiav.NewIOContextFlags(astiav.IOContextFlagWrite))
|
||||
require.NoError(t, err)
|
||||
c.Write(nil)
|
||||
c.Write([]byte("test"))
|
||||
err = c.Closep()
|
||||
require.NoError(t, err)
|
||||
b, err := ioutil.ReadFile(path)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "test", string(b))
|
||||
err = os.Remove(path)
|
||||
require.NoError(t, err)
|
||||
}
|
28
io_format_flag.go
Normal file
28
io_format_flag.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package astiav
|
||||
|
||||
//#cgo pkg-config: libavformat
|
||||
//#include <libavformat/avformat.h>
|
||||
import "C"
|
||||
|
||||
type IOFormatFlag int
|
||||
|
||||
// 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)
|
||||
)
|
12
level.go
Normal file
12
level.go
Normal file
@@ -0,0 +1,12 @@
|
||||
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)
|
||||
)
|
79
log.go
Normal file
79
log.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package astiav
|
||||
|
||||
//#cgo pkg-config: libavutil
|
||||
//#include <libavutil/log.h>
|
||||
/*
|
||||
extern void goAstiavLogCallback(int level, char* msg, char* parent);
|
||||
|
||||
static inline void astiavLogCallback(void *avcl, int level, const char *fmt, va_list vl)
|
||||
{
|
||||
if (level > av_log_get_level()) return;
|
||||
AVClass* avc = avcl ? *(AVClass **) avcl : NULL;
|
||||
char parent[1024];
|
||||
if (avc) {
|
||||
sprintf(parent, "%p", avcl);
|
||||
}
|
||||
char msg[1024];
|
||||
vsprintf(msg, fmt, vl);
|
||||
goAstiavLogCallback(level, msg, parent);
|
||||
}
|
||||
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(int level, const char *fmt)
|
||||
{
|
||||
av_log(NULL, level, fmt, NULL);
|
||||
}
|
||||
*/
|
||||
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))
|
||||
}
|
||||
|
||||
type LogCallback func(l LogLevel, msg, parent string)
|
||||
|
||||
var logCallback LogCallback
|
||||
|
||||
func SetLogCallback(c LogCallback) {
|
||||
logCallback = c
|
||||
C.astiavSetLogCallback()
|
||||
}
|
||||
|
||||
//export goAstiavLogCallback
|
||||
func goAstiavLogCallback(level C.int, msg, parent *C.char) {
|
||||
if logCallback == nil {
|
||||
return
|
||||
}
|
||||
logCallback(LogLevel(level), C.GoString(msg), C.GoString(parent))
|
||||
}
|
||||
|
||||
func ResetLogCallback() {
|
||||
C.astiavResetLogCallback()
|
||||
}
|
||||
|
||||
func Log(l LogLevel, msg string) {
|
||||
msgc := C.CString(msg)
|
||||
defer C.free(unsafe.Pointer(msgc))
|
||||
C.astiavLog(C.int(l), msgc)
|
||||
}
|
46
log_test.go
Normal file
46
log_test.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package astiav_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/asticode/go-astiav"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type logItem struct {
|
||||
l astiav.LogLevel
|
||||
msg string
|
||||
}
|
||||
|
||||
func TestLog(t *testing.T) {
|
||||
var lis []logItem
|
||||
astiav.SetLogCallback(func(l astiav.LogLevel, msg, parent string) {
|
||||
lis = append(lis, logItem{
|
||||
l: l,
|
||||
msg: msg,
|
||||
})
|
||||
})
|
||||
astiav.SetLogLevel(astiav.LogLevelWarning)
|
||||
astiav.Log(astiav.LogLevelInfo, "info")
|
||||
astiav.Log(astiav.LogLevelWarning, "warning")
|
||||
astiav.Log(astiav.LogLevelError, "error")
|
||||
astiav.Log(astiav.LogLevelFatal, "fatal")
|
||||
require.Equal(t, []logItem{
|
||||
{
|
||||
l: astiav.LogLevelWarning,
|
||||
msg: "warning",
|
||||
},
|
||||
{
|
||||
l: astiav.LogLevelError,
|
||||
msg: "error",
|
||||
},
|
||||
{
|
||||
l: astiav.LogLevelFatal,
|
||||
msg: "fatal",
|
||||
},
|
||||
}, lis)
|
||||
astiav.ResetLogCallback()
|
||||
lis = []logItem{}
|
||||
astiav.Log(astiav.LogLevelError, "test error log\n")
|
||||
require.Equal(t, []logItem{}, lis)
|
||||
}
|
13
mathematics.go
Normal file
13
mathematics.go
Normal file
@@ -0,0 +1,13 @@
|
||||
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)))
|
||||
}
|
14
mathematics_test.go
Normal file
14
mathematics_test.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package astiav_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/asticode/go-astiav"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMathematics(t *testing.T) {
|
||||
require.Equal(t, int64(1000), astiav.RescaleQ(100, astiav.NewRational(1, 100), astiav.NewRational(1, 1000)))
|
||||
require.Equal(t, int64(0), astiav.RescaleQRnd(1, astiav.NewRational(1, 100), astiav.NewRational(1, 10), astiav.RoundingDown))
|
||||
require.Equal(t, int64(1), astiav.RescaleQRnd(1, astiav.NewRational(1, 100), astiav.NewRational(1, 10), astiav.RoundingUp))
|
||||
}
|
23
media_type.go
Normal file
23
media_type.go
Normal file
@@ -0,0 +1,23 @@
|
||||
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)))
|
||||
}
|
12
media_type_test.go
Normal file
12
media_type_test.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package astiav_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/asticode/go-astiav"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMediaType(t *testing.T) {
|
||||
require.Equal(t, "video", astiav.MediaTypeVideo.String())
|
||||
}
|
21
output_format.go
Normal file
21
output_format.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package astiav
|
||||
|
||||
//#cgo pkg-config: libavformat
|
||||
//#include <libavformat/avformat.h>
|
||||
import "C"
|
||||
|
||||
// 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 (f *OutputFormat) Flags() IOFormatFlags {
|
||||
return IOFormatFlags(f.c.flags)
|
||||
}
|
121
packet.go
Normal file
121
packet.go
Normal file
@@ -0,0 +1,121 @@
|
||||
package astiav
|
||||
|
||||
//#cgo pkg-config: libavcodec
|
||||
//#include <libavcodec/avcodec.h>
|
||||
import "C"
|
||||
|
||||
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavcodec/packet.h#L350
|
||||
type Packet struct {
|
||||
c *C.struct_AVPacket
|
||||
}
|
||||
|
||||
func newPacketFromC(c *C.struct_AVPacket) *Packet {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return &Packet{c: c}
|
||||
}
|
||||
|
||||
func AllocPacket() *Packet {
|
||||
return newPacketFromC(C.av_packet_alloc())
|
||||
}
|
||||
|
||||
func (p *Packet) Data() []byte {
|
||||
if p.c.data == nil {
|
||||
return nil
|
||||
}
|
||||
return bytesFromC(func(size *C.int) *C.uint8_t {
|
||||
*size = p.c.size
|
||||
return p.c.data
|
||||
})
|
||||
}
|
||||
|
||||
func (p *Packet) Dts() int64 {
|
||||
return int64(p.c.dts)
|
||||
}
|
||||
|
||||
func (p *Packet) SetDts(v int64) {
|
||||
p.c.dts = C.int64_t(v)
|
||||
}
|
||||
|
||||
func (p *Packet) Duration() int64 {
|
||||
return int64(p.c.duration)
|
||||
}
|
||||
|
||||
func (p *Packet) SetDuration(d int64) {
|
||||
p.c.duration = C.int64_t(d)
|
||||
}
|
||||
|
||||
func (p *Packet) Flags() PacketFlags {
|
||||
return PacketFlags(p.c.flags)
|
||||
}
|
||||
|
||||
func (p *Packet) SetFlags(f PacketFlags) {
|
||||
p.c.flags = C.int(f)
|
||||
}
|
||||
|
||||
func (p *Packet) Pos() int64 {
|
||||
return int64(p.c.pos)
|
||||
}
|
||||
|
||||
func (p *Packet) SetPos(v int64) {
|
||||
p.c.pos = C.int64_t(v)
|
||||
}
|
||||
|
||||
func (p *Packet) Pts() int64 {
|
||||
return int64(p.c.pts)
|
||||
}
|
||||
|
||||
func (p *Packet) SetPts(v int64) {
|
||||
p.c.pts = C.int64_t(v)
|
||||
}
|
||||
|
||||
func (p *Packet) SideData(t PacketSideDataType) []byte {
|
||||
return bytesFromC(func(size *C.int) *C.uint8_t {
|
||||
return C.av_packet_get_side_data(p.c, (C.enum_AVPacketSideDataType)(t), size)
|
||||
})
|
||||
}
|
||||
|
||||
func (p *Packet) Size() int {
|
||||
return int(p.c.size)
|
||||
}
|
||||
|
||||
func (p *Packet) SetSize(s int) {
|
||||
p.c.size = C.int(s)
|
||||
}
|
||||
|
||||
func (p *Packet) StreamIndex() int {
|
||||
return int(p.c.stream_index)
|
||||
}
|
||||
|
||||
func (p *Packet) SetStreamIndex(i int) {
|
||||
p.c.stream_index = C.int(i)
|
||||
}
|
||||
|
||||
func (p *Packet) Free() {
|
||||
C.av_packet_free(&p.c)
|
||||
}
|
||||
|
||||
func (p *Packet) Clone() *Packet {
|
||||
return newPacketFromC(C.av_packet_clone(p.c))
|
||||
}
|
||||
|
||||
func (p *Packet) AllocPayload(s int) error {
|
||||
return newError(C.av_new_packet(p.c, C.int(s)))
|
||||
}
|
||||
|
||||
func (p *Packet) Ref(src *Packet) error {
|
||||
return newError(C.av_packet_ref(p.c, src.c))
|
||||
}
|
||||
|
||||
func (p *Packet) Unref() {
|
||||
C.av_packet_unref(p.c)
|
||||
}
|
||||
|
||||
func (p *Packet) MoveRef(src *Packet) {
|
||||
C.av_packet_move_ref(p.c, src.c)
|
||||
}
|
||||
|
||||
func (p *Packet) RescaleTs(src, dst Rational) {
|
||||
C.av_packet_rescale_ts(p.c, src.c, dst.c)
|
||||
}
|
14
packet_flag.go
Normal file
14
packet_flag.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package astiav
|
||||
|
||||
//#cgo pkg-config: libavcodec
|
||||
//#include <libavcodec/avcodec.h>
|
||||
import "C"
|
||||
|
||||
type PacketFlag int
|
||||
|
||||
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavcodec/packet.h#L428
|
||||
const (
|
||||
PacketFlagCorrupt = PacketFlag(C.AV_PKT_FLAG_CORRUPT)
|
||||
PacketFlagDiscard = PacketFlag(C.AV_PKT_FLAG_DISCARD)
|
||||
PacketFlagKey = PacketFlag(C.AV_PKT_FLAG_KEY)
|
||||
)
|
39
packet_side_data_type.go
Normal file
39
packet_side_data_type.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package astiav
|
||||
|
||||
//#cgo pkg-config: libavcodec
|
||||
//#include <libavcodec/avcodec.h>
|
||||
import "C"
|
||||
|
||||
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavcodec/packet.h#L40
|
||||
type PacketSideDataType C.enum_AVPacketSideDataType
|
||||
|
||||
const (
|
||||
PacketSideDataTypeA53Cc = PacketSideDataType(C.AV_PKT_DATA_A53_CC)
|
||||
PacketSideDataTypeAfd = PacketSideDataType(C.AV_PKT_DATA_AFD)
|
||||
PacketSideDataTypeAudioServiceType = PacketSideDataType(C.AV_PKT_DATA_AUDIO_SERVICE_TYPE)
|
||||
PacketSideDataTypeContentLightLevel = PacketSideDataType(C.AV_PKT_DATA_CONTENT_LIGHT_LEVEL)
|
||||
PacketSideDataTypeCpbProperties = PacketSideDataType(C.AV_PKT_DATA_CPB_PROPERTIES)
|
||||
PacketSideDataTypeDisplaymatrix = PacketSideDataType(C.AV_PKT_DATA_DISPLAYMATRIX)
|
||||
PacketSideDataTypeEncryptionInfo = PacketSideDataType(C.AV_PKT_DATA_ENCRYPTION_INFO)
|
||||
PacketSideDataTypeEncryptionInitInfo = PacketSideDataType(C.AV_PKT_DATA_ENCRYPTION_INIT_INFO)
|
||||
PacketSideDataTypeFallbackTrack = PacketSideDataType(C.AV_PKT_DATA_FALLBACK_TRACK)
|
||||
PacketSideDataTypeH263MbInfo = PacketSideDataType(C.AV_PKT_DATA_H263_MB_INFO)
|
||||
PacketSideDataTypeJpDualmono = PacketSideDataType(C.AV_PKT_DATA_JP_DUALMONO)
|
||||
PacketSideDataTypeMasteringDisplayMetadata = PacketSideDataType(C.AV_PKT_DATA_MASTERING_DISPLAY_METADATA)
|
||||
PacketSideDataTypeMatroskaBlockadditional = PacketSideDataType(C.AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL)
|
||||
PacketSideDataTypeMetadataUpdate = PacketSideDataType(C.AV_PKT_DATA_METADATA_UPDATE)
|
||||
PacketSideDataTypeMpegtsStreamId = PacketSideDataType(C.AV_PKT_DATA_MPEGTS_STREAM_ID)
|
||||
PacketSideDataTypeNb = PacketSideDataType(C.AV_PKT_DATA_NB)
|
||||
PacketSideDataTypeNewExtradata = PacketSideDataType(C.AV_PKT_DATA_NEW_EXTRADATA)
|
||||
PacketSideDataTypePalette = PacketSideDataType(C.AV_PKT_DATA_PALETTE)
|
||||
PacketSideDataTypeParamChange = PacketSideDataType(C.AV_PKT_DATA_PARAM_CHANGE)
|
||||
PacketSideDataTypeQualityStats = PacketSideDataType(C.AV_PKT_DATA_QUALITY_STATS)
|
||||
PacketSideDataTypeReplaygain = PacketSideDataType(C.AV_PKT_DATA_REPLAYGAIN)
|
||||
PacketSideDataTypeSkipSamples = PacketSideDataType(C.AV_PKT_DATA_SKIP_SAMPLES)
|
||||
PacketSideDataTypeSpherical = PacketSideDataType(C.AV_PKT_DATA_SPHERICAL)
|
||||
PacketSideDataTypeStereo3D = PacketSideDataType(C.AV_PKT_DATA_STEREO3D)
|
||||
PacketSideDataTypeStringsMetadata = PacketSideDataType(C.AV_PKT_DATA_STRINGS_METADATA)
|
||||
PacketSideDataTypeSubtitlePosition = PacketSideDataType(C.AV_PKT_DATA_SUBTITLE_POSITION)
|
||||
PacketSideDataTypeWebvttIdentifier = PacketSideDataType(C.AV_PKT_DATA_WEBVTT_IDENTIFIER)
|
||||
PacketSideDataTypeWebvttSettings = PacketSideDataType(C.AV_PKT_DATA_WEBVTT_SETTINGS)
|
||||
)
|
98
packet_test.go
Normal file
98
packet_test.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package astiav_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/asticode/go-astiav"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func videoInputFirstPacket() (pkt *astiav.Packet, err error) {
|
||||
if global.pkt != nil {
|
||||
return global.pkt, nil
|
||||
}
|
||||
|
||||
var fc *astiav.FormatContext
|
||||
if fc, err = videoInputFormatContext(); err != nil {
|
||||
err = fmt.Errorf("astiav_test: getting input format context failed")
|
||||
return
|
||||
}
|
||||
|
||||
pkt = astiav.AllocPacket()
|
||||
if pkt == nil {
|
||||
err = errors.New("astiav_test: pkt is nil")
|
||||
return
|
||||
}
|
||||
global.closer.Add(pkt.Free)
|
||||
|
||||
if err = fc.ReadFrame(pkt); err != nil {
|
||||
err = fmt.Errorf("astiav_test: reading frame failed: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
global.pkt = pkt
|
||||
return
|
||||
}
|
||||
|
||||
func TestPacket(t *testing.T) {
|
||||
pkt1, err := videoInputFirstPacket()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, []byte{0x0, 0x0, 0x0, 0xd1, 0x65, 0x88, 0x82, 0x0, 0x1f, 0x5f, 0xff, 0xf8, 0x22, 0x8a, 0x0, 0x2, 0x2d, 0xbe, 0x38, 0xc7, 0x19, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x3a, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xb9, 0xb8, 0xe6, 0x39, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xc0}, pkt1.Data())
|
||||
require.Equal(t, int64(0), pkt1.Dts())
|
||||
require.Equal(t, int64(512), pkt1.Duration())
|
||||
require.True(t, pkt1.Flags().Has(astiav.PacketFlagKey))
|
||||
require.Equal(t, int64(48), pkt1.Pos())
|
||||
require.Equal(t, int64(0), pkt1.Pts())
|
||||
require.Equal(t, 213, pkt1.Size())
|
||||
require.Equal(t, 0, pkt1.StreamIndex())
|
||||
|
||||
pkt2 := astiav.AllocPacket()
|
||||
require.NotNil(t, pkt2)
|
||||
defer pkt2.Free()
|
||||
require.Nil(t, pkt2.Data())
|
||||
err = pkt2.AllocPayload(5)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, pkt2.Data(), 5)
|
||||
pkt2.SetDts(1)
|
||||
pkt2.SetDuration(2)
|
||||
pkt2.SetFlags(astiav.NewPacketFlags(3))
|
||||
pkt2.SetPos(4)
|
||||
pkt2.SetPts(5)
|
||||
pkt2.SetSize(6)
|
||||
pkt2.SetStreamIndex(7)
|
||||
require.Equal(t, int64(1), pkt2.Dts())
|
||||
require.Equal(t, int64(2), pkt2.Duration())
|
||||
require.Equal(t, astiav.NewPacketFlags(3), pkt2.Flags())
|
||||
require.Equal(t, int64(4), pkt2.Pos())
|
||||
require.Equal(t, int64(5), pkt2.Pts())
|
||||
require.Equal(t, 6, pkt2.Size())
|
||||
require.Equal(t, 7, pkt2.StreamIndex())
|
||||
|
||||
pkt3 := pkt1.Clone()
|
||||
require.NotNil(t, pkt3)
|
||||
defer pkt3.Free()
|
||||
require.Equal(t, int64(512), pkt3.Duration())
|
||||
|
||||
err = pkt3.Ref(pkt2)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int64(2), pkt3.Duration())
|
||||
|
||||
pkt3.MoveRef(pkt1)
|
||||
require.Equal(t, int64(512), pkt3.Duration())
|
||||
require.Equal(t, int64(0), pkt1.Duration())
|
||||
|
||||
pkt3.Unref()
|
||||
require.Equal(t, int64(0), pkt3.Duration())
|
||||
|
||||
pkt3.SetDts(10)
|
||||
pkt3.SetDuration(20)
|
||||
pkt3.SetPts(30)
|
||||
pkt3.RescaleTs(astiav.NewRational(1, 10), astiav.NewRational(1, 1))
|
||||
require.Equal(t, int64(1), pkt3.Dts())
|
||||
require.Equal(t, int64(2), pkt3.Duration())
|
||||
require.Equal(t, int64(3), pkt3.Pts())
|
||||
|
||||
// TODO Test SideData
|
||||
}
|
23
picture_type.go
Normal file
23
picture_type.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package astiav
|
||||
|
||||
//#cgo pkg-config: libavutil
|
||||
//#include <libavutil/avutil.h>
|
||||
import "C"
|
||||
|
||||
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavutil/avutil.h#L272
|
||||
type PictureType C.enum_AVPictureType
|
||||
|
||||
const (
|
||||
PictureTypeNone = PictureType(C.AV_PICTURE_TYPE_NONE)
|
||||
PictureTypeI = PictureType(C.AV_PICTURE_TYPE_I)
|
||||
PictureTypeP = PictureType(C.AV_PICTURE_TYPE_P)
|
||||
PictureTypeB = PictureType(C.AV_PICTURE_TYPE_B)
|
||||
PictureTypeS = PictureType(C.AV_PICTURE_TYPE_S)
|
||||
PictureTypeSi = PictureType(C.AV_PICTURE_TYPE_SI)
|
||||
PictureTypeSp = PictureType(C.AV_PICTURE_TYPE_SP)
|
||||
PictureTypeBi = PictureType(C.AV_PICTURE_TYPE_BI)
|
||||
)
|
||||
|
||||
func (t PictureType) String() string {
|
||||
return string(rune(C.av_get_picture_type_char((C.enum_AVPictureType)(t))))
|
||||
}
|
12
picture_type_test.go
Normal file
12
picture_type_test.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package astiav_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/asticode/go-astiav"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPictureType(t *testing.T) {
|
||||
require.Equal(t, "I", astiav.PictureTypeI.String())
|
||||
}
|
212
pixel_format.go
Normal file
212
pixel_format.go
Normal file
@@ -0,0 +1,212 @@
|
||||
package astiav
|
||||
|
||||
//#cgo pkg-config: libavcodec libavutil
|
||||
//#include <libavcodec/avcodec.h>
|
||||
//#include <libavutil/avutil.h>
|
||||
//#include <libavutil/pixdesc.h>
|
||||
//#include <libavutil/pixfmt.h>
|
||||
import "C"
|
||||
|
||||
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavutil/pixfmt.h#L64
|
||||
type PixelFormat C.enum_AVPixelFormat
|
||||
|
||||
const (
|
||||
PixelFormat0Bgr = PixelFormat(C.AV_PIX_FMT_0BGR)
|
||||
PixelFormat0Rgb = PixelFormat(C.AV_PIX_FMT_0RGB)
|
||||
PixelFormatAbgr = PixelFormat(C.AV_PIX_FMT_ABGR)
|
||||
PixelFormatArgb = PixelFormat(C.AV_PIX_FMT_ARGB)
|
||||
PixelFormatAyuv64Be = PixelFormat(C.AV_PIX_FMT_AYUV64BE)
|
||||
PixelFormatAyuv64Le = PixelFormat(C.AV_PIX_FMT_AYUV64LE)
|
||||
PixelFormatBayerBggr16Be = PixelFormat(C.AV_PIX_FMT_BAYER_BGGR16BE)
|
||||
PixelFormatBayerBggr16Le = PixelFormat(C.AV_PIX_FMT_BAYER_BGGR16LE)
|
||||
PixelFormatBayerBggr8 = PixelFormat(C.AV_PIX_FMT_BAYER_BGGR8)
|
||||
PixelFormatBayerGbrg16Be = PixelFormat(C.AV_PIX_FMT_BAYER_GBRG16BE)
|
||||
PixelFormatBayerGbrg16Le = PixelFormat(C.AV_PIX_FMT_BAYER_GBRG16LE)
|
||||
PixelFormatBayerGbrg8 = PixelFormat(C.AV_PIX_FMT_BAYER_GBRG8)
|
||||
PixelFormatBayerGrbg16Be = PixelFormat(C.AV_PIX_FMT_BAYER_GRBG16BE)
|
||||
PixelFormatBayerGrbg16Le = PixelFormat(C.AV_PIX_FMT_BAYER_GRBG16LE)
|
||||
PixelFormatBayerGrbg8 = PixelFormat(C.AV_PIX_FMT_BAYER_GRBG8)
|
||||
PixelFormatBayerRggb16Be = PixelFormat(C.AV_PIX_FMT_BAYER_RGGB16BE)
|
||||
PixelFormatBayerRggb16Le = PixelFormat(C.AV_PIX_FMT_BAYER_RGGB16LE)
|
||||
PixelFormatBayerRggb8 = PixelFormat(C.AV_PIX_FMT_BAYER_RGGB8)
|
||||
PixelFormatBgr0 = PixelFormat(C.AV_PIX_FMT_BGR0)
|
||||
PixelFormatBgr24 = PixelFormat(C.AV_PIX_FMT_BGR24)
|
||||
PixelFormatBgr4 = PixelFormat(C.AV_PIX_FMT_BGR4)
|
||||
PixelFormatBgr444Be = PixelFormat(C.AV_PIX_FMT_BGR444BE)
|
||||
PixelFormatBgr444Le = PixelFormat(C.AV_PIX_FMT_BGR444LE)
|
||||
PixelFormatBgr48Be = PixelFormat(C.AV_PIX_FMT_BGR48BE)
|
||||
PixelFormatBgr48Le = PixelFormat(C.AV_PIX_FMT_BGR48LE)
|
||||
PixelFormatBgr4Byte = PixelFormat(C.AV_PIX_FMT_BGR4_BYTE)
|
||||
PixelFormatBgr555Be = PixelFormat(C.AV_PIX_FMT_BGR555BE)
|
||||
PixelFormatBgr555Le = PixelFormat(C.AV_PIX_FMT_BGR555LE)
|
||||
PixelFormatBgr565Be = PixelFormat(C.AV_PIX_FMT_BGR565BE)
|
||||
PixelFormatBgr565Le = PixelFormat(C.AV_PIX_FMT_BGR565LE)
|
||||
PixelFormatBgr8 = PixelFormat(C.AV_PIX_FMT_BGR8)
|
||||
PixelFormatBgra = PixelFormat(C.AV_PIX_FMT_BGRA)
|
||||
PixelFormatBgra64Be = PixelFormat(C.AV_PIX_FMT_BGRA64BE)
|
||||
PixelFormatBgra64Le = PixelFormat(C.AV_PIX_FMT_BGRA64LE)
|
||||
PixelFormatCuda = PixelFormat(C.AV_PIX_FMT_CUDA)
|
||||
PixelFormatD3D11 = PixelFormat(C.AV_PIX_FMT_D3D11)
|
||||
PixelFormatD3D11VaVld = PixelFormat(C.AV_PIX_FMT_D3D11VA_VLD)
|
||||
PixelFormatDrmPrime = PixelFormat(C.AV_PIX_FMT_DRM_PRIME)
|
||||
PixelFormatDxva2Vld = PixelFormat(C.AV_PIX_FMT_DXVA2_VLD)
|
||||
PixelFormatGbr24P = PixelFormat(C.AV_PIX_FMT_GBR24P)
|
||||
PixelFormatGbrap = PixelFormat(C.AV_PIX_FMT_GBRAP)
|
||||
PixelFormatGbrap10Be = PixelFormat(C.AV_PIX_FMT_GBRAP10BE)
|
||||
PixelFormatGbrap10Le = PixelFormat(C.AV_PIX_FMT_GBRAP10LE)
|
||||
PixelFormatGbrap12Be = PixelFormat(C.AV_PIX_FMT_GBRAP12BE)
|
||||
PixelFormatGbrap12Le = PixelFormat(C.AV_PIX_FMT_GBRAP12LE)
|
||||
PixelFormatGbrap16Be = PixelFormat(C.AV_PIX_FMT_GBRAP16BE)
|
||||
PixelFormatGbrap16Le = PixelFormat(C.AV_PIX_FMT_GBRAP16LE)
|
||||
PixelFormatGbrapf32Be = PixelFormat(C.AV_PIX_FMT_GBRAPF32BE)
|
||||
PixelFormatGbrapf32Le = PixelFormat(C.AV_PIX_FMT_GBRAPF32LE)
|
||||
PixelFormatGbrp = PixelFormat(C.AV_PIX_FMT_GBRP)
|
||||
PixelFormatGbrp10Be = PixelFormat(C.AV_PIX_FMT_GBRP10BE)
|
||||
PixelFormatGbrp10Le = PixelFormat(C.AV_PIX_FMT_GBRP10LE)
|
||||
PixelFormatGbrp12Be = PixelFormat(C.AV_PIX_FMT_GBRP12BE)
|
||||
PixelFormatGbrp12Le = PixelFormat(C.AV_PIX_FMT_GBRP12LE)
|
||||
PixelFormatGbrp14Be = PixelFormat(C.AV_PIX_FMT_GBRP14BE)
|
||||
PixelFormatGbrp14Le = PixelFormat(C.AV_PIX_FMT_GBRP14LE)
|
||||
PixelFormatGbrp16Be = PixelFormat(C.AV_PIX_FMT_GBRP16BE)
|
||||
PixelFormatGbrp16Le = PixelFormat(C.AV_PIX_FMT_GBRP16LE)
|
||||
PixelFormatGbrp9Be = PixelFormat(C.AV_PIX_FMT_GBRP9BE)
|
||||
PixelFormatGbrp9Le = PixelFormat(C.AV_PIX_FMT_GBRP9LE)
|
||||
PixelFormatGbrpf32Be = PixelFormat(C.AV_PIX_FMT_GBRPF32BE)
|
||||
PixelFormatGbrpf32Le = PixelFormat(C.AV_PIX_FMT_GBRPF32LE)
|
||||
PixelFormatGray10Be = PixelFormat(C.AV_PIX_FMT_GRAY10BE)
|
||||
PixelFormatGray10Le = PixelFormat(C.AV_PIX_FMT_GRAY10LE)
|
||||
PixelFormatGray12Be = PixelFormat(C.AV_PIX_FMT_GRAY12BE)
|
||||
PixelFormatGray12Le = PixelFormat(C.AV_PIX_FMT_GRAY12LE)
|
||||
PixelFormatGray14Be = PixelFormat(C.AV_PIX_FMT_GRAY14BE)
|
||||
PixelFormatGray14Le = PixelFormat(C.AV_PIX_FMT_GRAY14LE)
|
||||
PixelFormatGray16Be = PixelFormat(C.AV_PIX_FMT_GRAY16BE)
|
||||
PixelFormatGray16Le = PixelFormat(C.AV_PIX_FMT_GRAY16LE)
|
||||
PixelFormatGray8 = PixelFormat(C.AV_PIX_FMT_GRAY8)
|
||||
PixelFormatGray8A = PixelFormat(C.AV_PIX_FMT_GRAY8A)
|
||||
PixelFormatGray9Be = PixelFormat(C.AV_PIX_FMT_GRAY9BE)
|
||||
PixelFormatGray9Le = PixelFormat(C.AV_PIX_FMT_GRAY9LE)
|
||||
PixelFormatGrayf32Be = PixelFormat(C.AV_PIX_FMT_GRAYF32BE)
|
||||
PixelFormatGrayf32Le = PixelFormat(C.AV_PIX_FMT_GRAYF32LE)
|
||||
PixelFormatMediacodec = PixelFormat(C.AV_PIX_FMT_MEDIACODEC)
|
||||
PixelFormatMmal = PixelFormat(C.AV_PIX_FMT_MMAL)
|
||||
PixelFormatMonoblack = PixelFormat(C.AV_PIX_FMT_MONOBLACK)
|
||||
PixelFormatMonowhite = PixelFormat(C.AV_PIX_FMT_MONOWHITE)
|
||||
PixelFormatNb = PixelFormat(C.AV_PIX_FMT_NB)
|
||||
PixelFormatNone = PixelFormat(C.AV_PIX_FMT_NONE)
|
||||
PixelFormatNv12 = PixelFormat(C.AV_PIX_FMT_NV12)
|
||||
PixelFormatNv16 = PixelFormat(C.AV_PIX_FMT_NV16)
|
||||
PixelFormatNv20Be = PixelFormat(C.AV_PIX_FMT_NV20BE)
|
||||
PixelFormatNv20Le = PixelFormat(C.AV_PIX_FMT_NV20LE)
|
||||
PixelFormatNv21 = PixelFormat(C.AV_PIX_FMT_NV21)
|
||||
PixelFormatOpencl = PixelFormat(C.AV_PIX_FMT_OPENCL)
|
||||
PixelFormatP010Be = PixelFormat(C.AV_PIX_FMT_P010BE)
|
||||
PixelFormatP010Le = PixelFormat(C.AV_PIX_FMT_P010LE)
|
||||
PixelFormatP016Be = PixelFormat(C.AV_PIX_FMT_P016BE)
|
||||
PixelFormatP016Le = PixelFormat(C.AV_PIX_FMT_P016LE)
|
||||
PixelFormatPal8 = PixelFormat(C.AV_PIX_FMT_PAL8)
|
||||
PixelFormatQsv = PixelFormat(C.AV_PIX_FMT_QSV)
|
||||
PixelFormatRgb0 = PixelFormat(C.AV_PIX_FMT_RGB0)
|
||||
PixelFormatRgb24 = PixelFormat(C.AV_PIX_FMT_RGB24)
|
||||
PixelFormatRgb4 = PixelFormat(C.AV_PIX_FMT_RGB4)
|
||||
PixelFormatRgb444Be = PixelFormat(C.AV_PIX_FMT_RGB444BE)
|
||||
PixelFormatRgb444Le = PixelFormat(C.AV_PIX_FMT_RGB444LE)
|
||||
PixelFormatRgb48Be = PixelFormat(C.AV_PIX_FMT_RGB48BE)
|
||||
PixelFormatRgb48Le = PixelFormat(C.AV_PIX_FMT_RGB48LE)
|
||||
PixelFormatRgb4Byte = PixelFormat(C.AV_PIX_FMT_RGB4_BYTE)
|
||||
PixelFormatRgb555Be = PixelFormat(C.AV_PIX_FMT_RGB555BE)
|
||||
PixelFormatRgb555Le = PixelFormat(C.AV_PIX_FMT_RGB555LE)
|
||||
PixelFormatRgb565Be = PixelFormat(C.AV_PIX_FMT_RGB565BE)
|
||||
PixelFormatRgb565Le = PixelFormat(C.AV_PIX_FMT_RGB565LE)
|
||||
PixelFormatRgb8 = PixelFormat(C.AV_PIX_FMT_RGB8)
|
||||
PixelFormatRgba = PixelFormat(C.AV_PIX_FMT_RGBA)
|
||||
PixelFormatRgba64Be = PixelFormat(C.AV_PIX_FMT_RGBA64BE)
|
||||
PixelFormatRgba64Le = PixelFormat(C.AV_PIX_FMT_RGBA64LE)
|
||||
PixelFormatUyvy422 = PixelFormat(C.AV_PIX_FMT_UYVY422)
|
||||
PixelFormatUyyvyy411 = PixelFormat(C.AV_PIX_FMT_UYYVYY411)
|
||||
PixelFormatVaapi = PixelFormat(C.AV_PIX_FMT_VAAPI)
|
||||
PixelFormatVdpau = PixelFormat(C.AV_PIX_FMT_VDPAU)
|
||||
PixelFormatVideotoolbox = PixelFormat(C.AV_PIX_FMT_VIDEOTOOLBOX)
|
||||
PixelFormatXvmc = PixelFormat(C.AV_PIX_FMT_XVMC)
|
||||
PixelFormatXyz12Be = PixelFormat(C.AV_PIX_FMT_XYZ12BE)
|
||||
PixelFormatXyz12Le = PixelFormat(C.AV_PIX_FMT_XYZ12LE)
|
||||
PixelFormatY400A = PixelFormat(C.AV_PIX_FMT_Y400A)
|
||||
PixelFormatYa16Be = PixelFormat(C.AV_PIX_FMT_YA16BE)
|
||||
PixelFormatYa16Le = PixelFormat(C.AV_PIX_FMT_YA16LE)
|
||||
PixelFormatYa8 = PixelFormat(C.AV_PIX_FMT_YA8)
|
||||
PixelFormatYuv410P = PixelFormat(C.AV_PIX_FMT_YUV410P)
|
||||
PixelFormatYuv411P = PixelFormat(C.AV_PIX_FMT_YUV411P)
|
||||
PixelFormatYuv420P = PixelFormat(C.AV_PIX_FMT_YUV420P)
|
||||
PixelFormatYuv420P10Be = PixelFormat(C.AV_PIX_FMT_YUV420P10BE)
|
||||
PixelFormatYuv420P10Le = PixelFormat(C.AV_PIX_FMT_YUV420P10LE)
|
||||
PixelFormatYuv420P12Be = PixelFormat(C.AV_PIX_FMT_YUV420P12BE)
|
||||
PixelFormatYuv420P12Le = PixelFormat(C.AV_PIX_FMT_YUV420P12LE)
|
||||
PixelFormatYuv420P14Be = PixelFormat(C.AV_PIX_FMT_YUV420P14BE)
|
||||
PixelFormatYuv420P14Le = PixelFormat(C.AV_PIX_FMT_YUV420P14LE)
|
||||
PixelFormatYuv420P16Be = PixelFormat(C.AV_PIX_FMT_YUV420P16BE)
|
||||
PixelFormatYuv420P16Le = PixelFormat(C.AV_PIX_FMT_YUV420P16LE)
|
||||
PixelFormatYuv420P9Be = PixelFormat(C.AV_PIX_FMT_YUV420P9BE)
|
||||
PixelFormatYuv420P9Le = PixelFormat(C.AV_PIX_FMT_YUV420P9LE)
|
||||
PixelFormatYuv422P = PixelFormat(C.AV_PIX_FMT_YUV422P)
|
||||
PixelFormatYuv422P10Be = PixelFormat(C.AV_PIX_FMT_YUV422P10BE)
|
||||
PixelFormatYuv422P10Le = PixelFormat(C.AV_PIX_FMT_YUV422P10LE)
|
||||
PixelFormatYuv422P12Be = PixelFormat(C.AV_PIX_FMT_YUV422P12BE)
|
||||
PixelFormatYuv422P12Le = PixelFormat(C.AV_PIX_FMT_YUV422P12LE)
|
||||
PixelFormatYuv422P14Be = PixelFormat(C.AV_PIX_FMT_YUV422P14BE)
|
||||
PixelFormatYuv422P14Le = PixelFormat(C.AV_PIX_FMT_YUV422P14LE)
|
||||
PixelFormatYuv422P16Be = PixelFormat(C.AV_PIX_FMT_YUV422P16BE)
|
||||
PixelFormatYuv422P16Le = PixelFormat(C.AV_PIX_FMT_YUV422P16LE)
|
||||
PixelFormatYuv422P9Be = PixelFormat(C.AV_PIX_FMT_YUV422P9BE)
|
||||
PixelFormatYuv422P9Le = PixelFormat(C.AV_PIX_FMT_YUV422P9LE)
|
||||
PixelFormatYuv440P = PixelFormat(C.AV_PIX_FMT_YUV440P)
|
||||
PixelFormatYuv440P10Be = PixelFormat(C.AV_PIX_FMT_YUV440P10BE)
|
||||
PixelFormatYuv440P10Le = PixelFormat(C.AV_PIX_FMT_YUV440P10LE)
|
||||
PixelFormatYuv440P12Be = PixelFormat(C.AV_PIX_FMT_YUV440P12BE)
|
||||
PixelFormatYuv440P12Le = PixelFormat(C.AV_PIX_FMT_YUV440P12LE)
|
||||
PixelFormatYuv444P = PixelFormat(C.AV_PIX_FMT_YUV444P)
|
||||
PixelFormatYuv444P10Be = PixelFormat(C.AV_PIX_FMT_YUV444P10BE)
|
||||
PixelFormatYuv444P10Le = PixelFormat(C.AV_PIX_FMT_YUV444P10LE)
|
||||
PixelFormatYuv444P12Be = PixelFormat(C.AV_PIX_FMT_YUV444P12BE)
|
||||
PixelFormatYuv444P12Le = PixelFormat(C.AV_PIX_FMT_YUV444P12LE)
|
||||
PixelFormatYuv444P14Be = PixelFormat(C.AV_PIX_FMT_YUV444P14BE)
|
||||
PixelFormatYuv444P14Le = PixelFormat(C.AV_PIX_FMT_YUV444P14LE)
|
||||
PixelFormatYuv444P16Be = PixelFormat(C.AV_PIX_FMT_YUV444P16BE)
|
||||
PixelFormatYuv444P16Le = PixelFormat(C.AV_PIX_FMT_YUV444P16LE)
|
||||
PixelFormatYuv444P9Be = PixelFormat(C.AV_PIX_FMT_YUV444P9BE)
|
||||
PixelFormatYuv444P9Le = PixelFormat(C.AV_PIX_FMT_YUV444P9LE)
|
||||
PixelFormatYuva420P = PixelFormat(C.AV_PIX_FMT_YUVA420P)
|
||||
PixelFormatYuva420P10Be = PixelFormat(C.AV_PIX_FMT_YUVA420P10BE)
|
||||
PixelFormatYuva420P10Le = PixelFormat(C.AV_PIX_FMT_YUVA420P10LE)
|
||||
PixelFormatYuva420P16Be = PixelFormat(C.AV_PIX_FMT_YUVA420P16BE)
|
||||
PixelFormatYuva420P16Le = PixelFormat(C.AV_PIX_FMT_YUVA420P16LE)
|
||||
PixelFormatYuva420P9Be = PixelFormat(C.AV_PIX_FMT_YUVA420P9BE)
|
||||
PixelFormatYuva420P9Le = PixelFormat(C.AV_PIX_FMT_YUVA420P9LE)
|
||||
PixelFormatYuva422P = PixelFormat(C.AV_PIX_FMT_YUVA422P)
|
||||
PixelFormatYuva422P10Be = PixelFormat(C.AV_PIX_FMT_YUVA422P10BE)
|
||||
PixelFormatYuva422P10Le = PixelFormat(C.AV_PIX_FMT_YUVA422P10LE)
|
||||
PixelFormatYuva422P16Be = PixelFormat(C.AV_PIX_FMT_YUVA422P16BE)
|
||||
PixelFormatYuva422P16Le = PixelFormat(C.AV_PIX_FMT_YUVA422P16LE)
|
||||
PixelFormatYuva422P9Be = PixelFormat(C.AV_PIX_FMT_YUVA422P9BE)
|
||||
PixelFormatYuva422P9Le = PixelFormat(C.AV_PIX_FMT_YUVA422P9LE)
|
||||
PixelFormatYuva444P = PixelFormat(C.AV_PIX_FMT_YUVA444P)
|
||||
PixelFormatYuva444P10Be = PixelFormat(C.AV_PIX_FMT_YUVA444P10BE)
|
||||
PixelFormatYuva444P10Le = PixelFormat(C.AV_PIX_FMT_YUVA444P10LE)
|
||||
PixelFormatYuva444P16Be = PixelFormat(C.AV_PIX_FMT_YUVA444P16BE)
|
||||
PixelFormatYuva444P16Le = PixelFormat(C.AV_PIX_FMT_YUVA444P16LE)
|
||||
PixelFormatYuva444P9Be = PixelFormat(C.AV_PIX_FMT_YUVA444P9BE)
|
||||
PixelFormatYuva444P9Le = PixelFormat(C.AV_PIX_FMT_YUVA444P9LE)
|
||||
PixelFormatYuvj411P = PixelFormat(C.AV_PIX_FMT_YUVJ411P)
|
||||
PixelFormatYuvj420P = PixelFormat(C.AV_PIX_FMT_YUVJ420P)
|
||||
PixelFormatYuvj422P = PixelFormat(C.AV_PIX_FMT_YUVJ422P)
|
||||
PixelFormatYuvj440P = PixelFormat(C.AV_PIX_FMT_YUVJ440P)
|
||||
PixelFormatYuvj444P = PixelFormat(C.AV_PIX_FMT_YUVJ444P)
|
||||
PixelFormatYuyv422 = PixelFormat(C.AV_PIX_FMT_YUYV422)
|
||||
PixelFormatYvyu422 = PixelFormat(C.AV_PIX_FMT_YVYU422)
|
||||
)
|
||||
|
||||
func (f PixelFormat) Name() string {
|
||||
return C.GoString(C.av_get_pix_fmt_name((C.enum_AVPixelFormat)(f)))
|
||||
}
|
||||
|
||||
func (f PixelFormat) String() string {
|
||||
return f.Name()
|
||||
}
|
12
pixel_format_test.go
Normal file
12
pixel_format_test.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package astiav_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/asticode/go-astiav"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPixelFormat(t *testing.T) {
|
||||
require.Equal(t, "yuv420p", astiav.PixelFormatYuv420P.String())
|
||||
}
|
100
profile.go
Normal file
100
profile.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package astiav
|
||||
|
||||
//#cgo pkg-config: libavcodec
|
||||
//#include <libavcodec/avcodec.h>
|
||||
import "C"
|
||||
|
||||
type Profile int
|
||||
|
||||
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavcodec/avcodec.h#L1526
|
||||
const (
|
||||
ProfileAacEld = Profile(C.FF_PROFILE_AAC_ELD)
|
||||
ProfileAacHe = Profile(C.FF_PROFILE_AAC_HE)
|
||||
ProfileAacHeV2 = Profile(C.FF_PROFILE_AAC_HE_V2)
|
||||
ProfileAacLd = Profile(C.FF_PROFILE_AAC_LD)
|
||||
ProfileAacLow = Profile(C.FF_PROFILE_AAC_LOW)
|
||||
ProfileAacLtp = Profile(C.FF_PROFILE_AAC_LTP)
|
||||
ProfileAacMain = Profile(C.FF_PROFILE_AAC_MAIN)
|
||||
ProfileAacSsr = Profile(C.FF_PROFILE_AAC_SSR)
|
||||
ProfileAv1High = Profile(C.FF_PROFILE_AV1_HIGH)
|
||||
ProfileAv1Main = Profile(C.FF_PROFILE_AV1_MAIN)
|
||||
ProfileAv1Professional = Profile(C.FF_PROFILE_AV1_PROFESSIONAL)
|
||||
ProfileDnxhd = Profile(C.FF_PROFILE_DNXHD)
|
||||
ProfileDnxhr444 = Profile(C.FF_PROFILE_DNXHR_444)
|
||||
ProfileDnxhrHq = Profile(C.FF_PROFILE_DNXHR_HQ)
|
||||
ProfileDnxhrHqx = Profile(C.FF_PROFILE_DNXHR_HQX)
|
||||
ProfileDnxhrLb = Profile(C.FF_PROFILE_DNXHR_LB)
|
||||
ProfileDnxhrSq = Profile(C.FF_PROFILE_DNXHR_SQ)
|
||||
ProfileDts = Profile(C.FF_PROFILE_DTS)
|
||||
ProfileDts9624 = Profile(C.FF_PROFILE_DTS_96_24)
|
||||
ProfileDtsEs = Profile(C.FF_PROFILE_DTS_ES)
|
||||
ProfileDtsExpress = Profile(C.FF_PROFILE_DTS_EXPRESS)
|
||||
ProfileDtsHdHra = Profile(C.FF_PROFILE_DTS_HD_HRA)
|
||||
ProfileDtsHdMa = Profile(C.FF_PROFILE_DTS_HD_MA)
|
||||
ProfileH264Baseline = Profile(C.FF_PROFILE_H264_BASELINE)
|
||||
ProfileH264Cavlc444 = Profile(C.FF_PROFILE_H264_CAVLC_444)
|
||||
ProfileH264Constrained = Profile(C.FF_PROFILE_H264_CONSTRAINED)
|
||||
ProfileH264ConstrainedBaseline = Profile(C.FF_PROFILE_H264_CONSTRAINED_BASELINE)
|
||||
ProfileH264Extended = Profile(C.FF_PROFILE_H264_EXTENDED)
|
||||
ProfileH264High = Profile(C.FF_PROFILE_H264_HIGH)
|
||||
ProfileH264High10 = Profile(C.FF_PROFILE_H264_HIGH_10)
|
||||
ProfileH264High10Intra = Profile(C.FF_PROFILE_H264_HIGH_10_INTRA)
|
||||
ProfileH264High422 = Profile(C.FF_PROFILE_H264_HIGH_422)
|
||||
ProfileH264High422Intra = Profile(C.FF_PROFILE_H264_HIGH_422_INTRA)
|
||||
ProfileH264High444 = Profile(C.FF_PROFILE_H264_HIGH_444)
|
||||
ProfileH264High444Intra = Profile(C.FF_PROFILE_H264_HIGH_444_INTRA)
|
||||
ProfileH264High444Predictive = Profile(C.FF_PROFILE_H264_HIGH_444_PREDICTIVE)
|
||||
ProfileH264Intra = Profile(C.FF_PROFILE_H264_INTRA)
|
||||
ProfileH264Main = Profile(C.FF_PROFILE_H264_MAIN)
|
||||
ProfileH264MultiviewHigh = Profile(C.FF_PROFILE_H264_MULTIVIEW_HIGH)
|
||||
ProfileH264StereoHigh = Profile(C.FF_PROFILE_H264_STEREO_HIGH)
|
||||
ProfileHevcMain = Profile(C.FF_PROFILE_HEVC_MAIN)
|
||||
ProfileHevcMain10 = Profile(C.FF_PROFILE_HEVC_MAIN_10)
|
||||
ProfileHevcMainStillPicture = Profile(C.FF_PROFILE_HEVC_MAIN_STILL_PICTURE)
|
||||
ProfileHevcRext = Profile(C.FF_PROFILE_HEVC_REXT)
|
||||
ProfileJpeg2000CstreamNoRestriction = Profile(C.FF_PROFILE_JPEG2000_CSTREAM_NO_RESTRICTION)
|
||||
ProfileJpeg2000CstreamRestriction0 = Profile(C.FF_PROFILE_JPEG2000_CSTREAM_RESTRICTION_0)
|
||||
ProfileJpeg2000CstreamRestriction1 = Profile(C.FF_PROFILE_JPEG2000_CSTREAM_RESTRICTION_1)
|
||||
ProfileJpeg2000Dcinema2K = Profile(C.FF_PROFILE_JPEG2000_DCINEMA_2K)
|
||||
ProfileJpeg2000Dcinema4K = Profile(C.FF_PROFILE_JPEG2000_DCINEMA_4K)
|
||||
ProfileMjpegHuffmanBaselineDct = Profile(C.FF_PROFILE_MJPEG_HUFFMAN_BASELINE_DCT)
|
||||
ProfileMjpegHuffmanExtendedSequentialDct = Profile(C.FF_PROFILE_MJPEG_HUFFMAN_EXTENDED_SEQUENTIAL_DCT)
|
||||
ProfileMjpegHuffmanLossless = Profile(C.FF_PROFILE_MJPEG_HUFFMAN_LOSSLESS)
|
||||
ProfileMjpegHuffmanProgressiveDct = Profile(C.FF_PROFILE_MJPEG_HUFFMAN_PROGRESSIVE_DCT)
|
||||
ProfileMjpegJpegLs = Profile(C.FF_PROFILE_MJPEG_JPEG_LS)
|
||||
ProfileMpeg2422 = Profile(C.FF_PROFILE_MPEG2_422)
|
||||
ProfileMpeg2AacHe = Profile(C.FF_PROFILE_MPEG2_AAC_HE)
|
||||
ProfileMpeg2AacLow = Profile(C.FF_PROFILE_MPEG2_AAC_LOW)
|
||||
ProfileMpeg2High = Profile(C.FF_PROFILE_MPEG2_HIGH)
|
||||
ProfileMpeg2Main = Profile(C.FF_PROFILE_MPEG2_MAIN)
|
||||
ProfileMpeg2Simple = Profile(C.FF_PROFILE_MPEG2_SIMPLE)
|
||||
ProfileMpeg2SnrScalable = Profile(C.FF_PROFILE_MPEG2_SNR_SCALABLE)
|
||||
ProfileMpeg2Ss = Profile(C.FF_PROFILE_MPEG2_SS)
|
||||
ProfileMpeg4AdvancedCoding = Profile(C.FF_PROFILE_MPEG4_ADVANCED_CODING)
|
||||
ProfileMpeg4AdvancedCore = Profile(C.FF_PROFILE_MPEG4_ADVANCED_CORE)
|
||||
ProfileMpeg4AdvancedRealTime = Profile(C.FF_PROFILE_MPEG4_ADVANCED_REAL_TIME)
|
||||
ProfileMpeg4AdvancedScalableTexture = Profile(C.FF_PROFILE_MPEG4_ADVANCED_SCALABLE_TEXTURE)
|
||||
ProfileMpeg4AdvancedSimple = Profile(C.FF_PROFILE_MPEG4_ADVANCED_SIMPLE)
|
||||
ProfileMpeg4BasicAnimatedTexture = Profile(C.FF_PROFILE_MPEG4_BASIC_ANIMATED_TEXTURE)
|
||||
ProfileMpeg4Core = Profile(C.FF_PROFILE_MPEG4_CORE)
|
||||
ProfileMpeg4CoreScalable = Profile(C.FF_PROFILE_MPEG4_CORE_SCALABLE)
|
||||
ProfileMpeg4Hybrid = Profile(C.FF_PROFILE_MPEG4_HYBRID)
|
||||
ProfileMpeg4Main = Profile(C.FF_PROFILE_MPEG4_MAIN)
|
||||
ProfileMpeg4NBit = Profile(C.FF_PROFILE_MPEG4_N_BIT)
|
||||
ProfileMpeg4ScalableTexture = Profile(C.FF_PROFILE_MPEG4_SCALABLE_TEXTURE)
|
||||
ProfileMpeg4Simple = Profile(C.FF_PROFILE_MPEG4_SIMPLE)
|
||||
ProfileMpeg4SimpleFaceAnimation = Profile(C.FF_PROFILE_MPEG4_SIMPLE_FACE_ANIMATION)
|
||||
ProfileMpeg4SimpleScalable = Profile(C.FF_PROFILE_MPEG4_SIMPLE_SCALABLE)
|
||||
ProfileMpeg4SimpleStudio = Profile(C.FF_PROFILE_MPEG4_SIMPLE_STUDIO)
|
||||
ProfileReserved = Profile(C.FF_PROFILE_RESERVED)
|
||||
ProfileSbcMsbc = Profile(C.FF_PROFILE_SBC_MSBC)
|
||||
ProfileUnknown = Profile(C.FF_PROFILE_UNKNOWN)
|
||||
ProfileVc1Advanced = Profile(C.FF_PROFILE_VC1_ADVANCED)
|
||||
ProfileVc1Complex = Profile(C.FF_PROFILE_VC1_COMPLEX)
|
||||
ProfileVc1Main = Profile(C.FF_PROFILE_VC1_MAIN)
|
||||
ProfileVc1Simple = Profile(C.FF_PROFILE_VC1_SIMPLE)
|
||||
ProfileVp90 = Profile(C.FF_PROFILE_VP9_0)
|
||||
ProfileVp91 = Profile(C.FF_PROFILE_VP9_1)
|
||||
ProfileVp92 = Profile(C.FF_PROFILE_VP9_2)
|
||||
ProfileVp93 = Profile(C.FF_PROFILE_VP9_3)
|
||||
)
|
52
rational.go
Normal file
52
rational.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package astiav
|
||||
|
||||
//#cgo pkg-config: libavutil
|
||||
//#include <libavutil/rational.h>
|
||||
import "C"
|
||||
import "strconv"
|
||||
|
||||
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavutil/rational.h#L58
|
||||
type Rational struct {
|
||||
c C.struct_AVRational
|
||||
}
|
||||
|
||||
func newRationalFromC(c C.struct_AVRational) Rational {
|
||||
return Rational{c: c}
|
||||
}
|
||||
|
||||
func NewRational(num, den int) Rational {
|
||||
var r Rational
|
||||
r.SetNum(num)
|
||||
r.SetDen(den)
|
||||
return r
|
||||
}
|
||||
|
||||
func (r Rational) Num() int {
|
||||
return int(r.c.num)
|
||||
}
|
||||
|
||||
func (r *Rational) SetNum(num int) {
|
||||
r.c.num = C.int(num)
|
||||
}
|
||||
|
||||
func (r Rational) Den() int {
|
||||
return int(r.c.den)
|
||||
}
|
||||
|
||||
func (r *Rational) SetDen(den int) {
|
||||
r.c.den = C.int(den)
|
||||
}
|
||||
|
||||
func (r Rational) ToDouble() float64 {
|
||||
if r.Num() == 0 || r.Den() == 0 {
|
||||
return 0
|
||||
}
|
||||
return float64(r.Num()) / float64(r.Den())
|
||||
}
|
||||
|
||||
func (r Rational) String() string {
|
||||
if r.Num() == 0 || r.Den() == 0 {
|
||||
return "0"
|
||||
}
|
||||
return strconv.Itoa(r.Num()) + "/" + strconv.Itoa(r.Den())
|
||||
}
|
23
rational_test.go
Normal file
23
rational_test.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package astiav_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/asticode/go-astiav"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestRational(t *testing.T) {
|
||||
r := astiav.NewRational(2, 1)
|
||||
require.Equal(t, 2, r.Num())
|
||||
require.Equal(t, 1, r.Den())
|
||||
r.SetNum(1)
|
||||
r.SetDen(2)
|
||||
require.Equal(t, 1, r.Num())
|
||||
require.Equal(t, 2, r.Den())
|
||||
require.Equal(t, "1/2", r.String())
|
||||
require.Equal(t, 0.5, r.ToDouble())
|
||||
r.SetDen(0)
|
||||
require.Equal(t, float64(0), r.ToDouble())
|
||||
require.Equal(t, "0", r.String())
|
||||
}
|
17
rounding.go
Normal file
17
rounding.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package astiav
|
||||
|
||||
//#cgo pkg-config: libavcodec libavutil
|
||||
//#include <libavutil/mathematics.h>
|
||||
import "C"
|
||||
|
||||
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavutil/mathematics.h#L79
|
||||
type Rounding C.enum_AVRounding
|
||||
|
||||
const (
|
||||
RoundingZero = Rounding(C.AV_ROUND_ZERO)
|
||||
RoundingInf = Rounding(C.AV_ROUND_INF)
|
||||
RoundingDown = Rounding(C.AV_ROUND_DOWN)
|
||||
RoundingUp = Rounding(C.AV_ROUND_UP)
|
||||
RoundingNearInf = Rounding(C.AV_ROUND_NEAR_INF)
|
||||
RoundingPassMinmax = Rounding(C.AV_ROUND_PASS_MINMAX)
|
||||
)
|
33
sample_format.go
Normal file
33
sample_format.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package astiav
|
||||
|
||||
//#cgo pkg-config: libavutil
|
||||
//#include <libavutil/samplefmt.h>
|
||||
import "C"
|
||||
|
||||
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavutil/samplefmt.h#L58
|
||||
type SampleFormat C.enum_AVSampleFormat
|
||||
|
||||
const (
|
||||
SampleFormatDbl = SampleFormat(C.AV_SAMPLE_FMT_DBL)
|
||||
SampleFormatDblp = SampleFormat(C.AV_SAMPLE_FMT_DBLP)
|
||||
SampleFormatFlt = SampleFormat(C.AV_SAMPLE_FMT_FLT)
|
||||
SampleFormatFltp = SampleFormat(C.AV_SAMPLE_FMT_FLTP)
|
||||
SampleFormatNb = SampleFormat(C.AV_SAMPLE_FMT_NB)
|
||||
SampleFormatNone = SampleFormat(C.AV_SAMPLE_FMT_NONE)
|
||||
SampleFormatS16 = SampleFormat(C.AV_SAMPLE_FMT_S16)
|
||||
SampleFormatS16P = SampleFormat(C.AV_SAMPLE_FMT_S16P)
|
||||
SampleFormatS32 = SampleFormat(C.AV_SAMPLE_FMT_S32)
|
||||
SampleFormatS32P = SampleFormat(C.AV_SAMPLE_FMT_S32P)
|
||||
SampleFormatS64 = SampleFormat(C.AV_SAMPLE_FMT_S64)
|
||||
SampleFormatS64P = SampleFormat(C.AV_SAMPLE_FMT_S64P)
|
||||
SampleFormatU8 = SampleFormat(C.AV_SAMPLE_FMT_U8)
|
||||
SampleFormatU8P = SampleFormat(C.AV_SAMPLE_FMT_U8P)
|
||||
)
|
||||
|
||||
func (f SampleFormat) Name() string {
|
||||
return C.GoString(C.av_get_sample_fmt_name((C.enum_AVSampleFormat)(f)))
|
||||
}
|
||||
|
||||
func (f SampleFormat) String() string {
|
||||
return f.Name()
|
||||
}
|
12
sample_format_test.go
Normal file
12
sample_format_test.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package astiav_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/asticode/go-astiav"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSampleFormat(t *testing.T) {
|
||||
require.Equal(t, "s16", astiav.SampleFormatS16.String())
|
||||
}
|
15
seek_flag.go
Normal file
15
seek_flag.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package astiav
|
||||
|
||||
//#cgo pkg-config: libavformat
|
||||
//#include <libavformat/avformat.h>
|
||||
import "C"
|
||||
|
||||
type SeekFlag int
|
||||
|
||||
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavformat/avformat.h#L2277
|
||||
const (
|
||||
SeekFlagAny = SeekFlag(C.AVSEEK_FLAG_ANY)
|
||||
SeekFlagBackward = SeekFlag(C.AVSEEK_FLAG_BACKWARD)
|
||||
SeekFlagByte = SeekFlag(C.AVSEEK_FLAG_BYTE)
|
||||
SeekFlagFrame = SeekFlag(C.AVSEEK_FLAG_FRAME)
|
||||
)
|
75
stream.go
Normal file
75
stream.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package astiav
|
||||
|
||||
//#cgo pkg-config: libavformat
|
||||
//#include <libavformat/avformat.h>
|
||||
import "C"
|
||||
|
||||
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavformat/avformat.h#L937
|
||||
type Stream struct {
|
||||
c *C.struct_AVStream
|
||||
}
|
||||
|
||||
func newStreamFromC(c *C.struct_AVStream) *Stream {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return &Stream{c: c}
|
||||
}
|
||||
|
||||
func (s *Stream) AvgFrameRate() Rational {
|
||||
return newRationalFromC(s.c.avg_frame_rate)
|
||||
}
|
||||
|
||||
func (s *Stream) CodecParameters() *CodecParameters {
|
||||
return newCodecParametersFromC(s.c.codecpar)
|
||||
}
|
||||
|
||||
func (s *Stream) Duration() int64 {
|
||||
return int64(s.c.duration)
|
||||
}
|
||||
|
||||
func (s *Stream) EventFlags() StreamEventFlags {
|
||||
return StreamEventFlags(s.c.event_flags)
|
||||
}
|
||||
|
||||
func (s *Stream) ID() int {
|
||||
return int(s.c.id)
|
||||
}
|
||||
|
||||
func (s *Stream) Index() int {
|
||||
return int(s.c.index)
|
||||
}
|
||||
|
||||
func (s *Stream) Metadata() *Dictionary {
|
||||
return newDictionaryFromC(s.c.metadata)
|
||||
}
|
||||
|
||||
func (s *Stream) NbFrames() int64 {
|
||||
return int64(s.c.nb_frames)
|
||||
}
|
||||
|
||||
func (s *Stream) RFrameRate() Rational {
|
||||
return newRationalFromC(s.c.r_frame_rate)
|
||||
}
|
||||
|
||||
func (s *Stream) SampleAspectRatio() Rational {
|
||||
return newRationalFromC(s.c.sample_aspect_ratio)
|
||||
}
|
||||
|
||||
func (s *Stream) SideData(t PacketSideDataType) []byte {
|
||||
return bytesFromC(func(size *C.int) *C.uint8_t {
|
||||
return C.av_stream_get_side_data(s.c, (C.enum_AVPacketSideDataType)(t), size)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Stream) StartTime() int64 {
|
||||
return int64(s.c.start_time)
|
||||
}
|
||||
|
||||
func (s *Stream) TimeBase() Rational {
|
||||
return newRationalFromC(s.c.time_base)
|
||||
}
|
||||
|
||||
func (s *Stream) SetTimeBase(r Rational) {
|
||||
s.c.time_base = r.c
|
||||
}
|
12
stream_event_flag.go
Normal file
12
stream_event_flag.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package astiav
|
||||
|
||||
//#cgo pkg-config: libavformat
|
||||
//#include <libavformat/avformat.h>
|
||||
import "C"
|
||||
|
||||
type StreamEventFlag int
|
||||
|
||||
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavformat/avformat.h#L1070
|
||||
const (
|
||||
StreamEventFlagMetadataUpdated = StreamEventFlag(C.AVSTREAM_EVENT_FLAG_METADATA_UPDATED)
|
||||
)
|
61
stream_test.go
Normal file
61
stream_test.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package astiav_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/asticode/go-astiav"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func videoInputStreams() (fc *astiav.FormatContext, s1, s2 *astiav.Stream, err error) {
|
||||
if global.inputFormatContext != nil && global.inputStream1 != nil && global.inputStream2 != nil {
|
||||
return global.inputFormatContext, global.inputStream1, global.inputStream2, nil
|
||||
}
|
||||
|
||||
if fc, err = videoInputFormatContext(); err != nil {
|
||||
err = fmt.Errorf("astiav_test: getting video input format context failed: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
ss := fc.Streams()
|
||||
if len(ss) < 2 {
|
||||
err = fmt.Errorf("astiav_test: invalid streams len: %d", len(ss))
|
||||
return
|
||||
}
|
||||
|
||||
s1 = ss[0]
|
||||
s2 = ss[1]
|
||||
|
||||
global.inputStream1 = s1
|
||||
global.inputStream2 = s2
|
||||
return
|
||||
}
|
||||
|
||||
func TestStream(t *testing.T) {
|
||||
_, s1, s2, err := videoInputStreams()
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, 0, s1.Index())
|
||||
require.Equal(t, astiav.NewRational(24, 1), s1.AvgFrameRate())
|
||||
require.Equal(t, int64(61440), s1.Duration())
|
||||
require.True(t, s1.EventFlags().Has(astiav.StreamEventFlag(2)))
|
||||
require.Equal(t, 1, s1.ID())
|
||||
require.Equal(t, "und", s1.Metadata().Get("language", nil, astiav.NewDictionaryFlags()).Value())
|
||||
require.Equal(t, int64(120), s1.NbFrames())
|
||||
require.Equal(t, astiav.NewRational(24, 1), s1.RFrameRate())
|
||||
require.Equal(t, astiav.NewRational(1, 1), s1.SampleAspectRatio())
|
||||
require.Equal(t, []byte{}, s1.SideData(astiav.PacketSideDataTypeNb))
|
||||
require.Equal(t, int64(0), s1.StartTime())
|
||||
require.Equal(t, astiav.NewRational(1, 12288), s1.TimeBase())
|
||||
|
||||
require.Equal(t, 1, s2.Index())
|
||||
require.Equal(t, int64(240640), s2.Duration())
|
||||
require.Equal(t, 2, s2.ID())
|
||||
require.Equal(t, int64(235), s2.NbFrames())
|
||||
require.Equal(t, int64(0), s2.StartTime())
|
||||
require.Equal(t, astiav.NewRational(1, 48000), s2.TimeBase())
|
||||
|
||||
s1.SetTimeBase(astiav.NewRational(1, 1))
|
||||
require.Equal(t, astiav.NewRational(1, 1), s1.TimeBase())
|
||||
}
|
16
strict_std_compliance.go
Normal file
16
strict_std_compliance.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package astiav
|
||||
|
||||
//#cgo pkg-config: libavcodec
|
||||
//#include <libavcodec/avcodec.h>
|
||||
import "C"
|
||||
|
||||
type StrictStdCompliance int
|
||||
|
||||
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavcodec/avcodec.h#L1281
|
||||
const (
|
||||
StrictStdComplianceVeryStrict = StrictStdCompliance(C.FF_COMPLIANCE_VERY_STRICT)
|
||||
StrictStdComplianceStrict = StrictStdCompliance(C.FF_COMPLIANCE_STRICT)
|
||||
StrictStdComplianceNormal = StrictStdCompliance(C.FF_COMPLIANCE_NORMAL)
|
||||
StrictStdComplianceUnofficial = StrictStdCompliance(C.FF_COMPLIANCE_UNOFFICIAL)
|
||||
StrictStdComplianceExperimental = StrictStdCompliance(C.FF_COMPLIANCE_EXPERIMENTAL)
|
||||
)
|
1
testdata/frame
vendored
Normal file
1
testdata/frame
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
testdata/video.mp4
vendored
Normal file
BIN
testdata/video.mp4
vendored
Normal file
Binary file not shown.
13
thread_type.go
Normal file
13
thread_type.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package astiav
|
||||
|
||||
//#cgo pkg-config: libavcodec
|
||||
//#include <libavcodec/avcodec.h>
|
||||
import "C"
|
||||
|
||||
type ThreadType int
|
||||
|
||||
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavcodec/avcodec.h#L1451
|
||||
const (
|
||||
ThreadTypeFrame = ThreadType(C.FF_THREAD_FRAME)
|
||||
ThreadTypeSlice = ThreadType(C.FF_THREAD_SLICE)
|
||||
)
|
Reference in New Issue
Block a user