Initial commit

This commit is contained in:
Quentin Renard
2022-02-05 10:27:03 +01:00
commit 477b147104
96 changed files with 5980 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
.DS_STORE
cover*
tmp

21
LICENSE Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View File

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

65
codec_test.go Normal file
View 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
View 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
View 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
View 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)
)

View 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
View 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
View 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
View 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
View 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
View 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
View 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))
}

View 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
View 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
View 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")
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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)))
})
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because one or more lines are too long

BIN
testdata/video.mp4 vendored Normal file

Binary file not shown.

13
thread_type.go Normal file
View 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)
)

14
time.go Normal file
View File

@@ -0,0 +1,14 @@
package astiav
//#cgo pkg-config: libavutil
//#include <libavutil/avutil.h>
import "C"
const (
NoPtsValue = int64(C.AV_NOPTS_VALUE)
TimeBase = int(C.AV_TIME_BASE)
)
var (
TimeBaseQ = newRationalFromC(C.AV_TIME_BASE_Q)
)