Refactored frame/packet side data + added regions of interest to frame side data

This commit is contained in:
Quentin Renard
2025-03-15 17:05:57 +01:00
parent 0d523ba53b
commit 584153d458
22 changed files with 295 additions and 197 deletions

View File

@@ -99,7 +99,5 @@ func TestCodecParameters(t *testing.T) {
b := []byte("test")
require.NoError(t, cp6.SetExtraData(b))
require.Equal(t, b, cp6.ExtraData())
sd := cp6.SideData()
require.NoError(t, sd.Add(PacketSideDataTypeDisplaymatrix, b))
require.Equal(t, b, sd.Get(PacketSideDataTypeDisplaymatrix))
testPacketSideData(cp6.SideData(), t)
}

View File

@@ -11,7 +11,7 @@ import (
// https://ffmpeg.org/doxygen/7.0/group__lavu__video__display.html
type DisplayMatrix [9]uint32
func NewDisplayMatrixFromBytes(b []byte) (m *DisplayMatrix, err error) {
func newDisplayMatrixFromBytes(b []byte) (m *DisplayMatrix, err error) {
// Check length
if len(b) < 36 {
err = fmt.Errorf("astiav: invalid length %d < 36", len(b))
@@ -35,7 +35,7 @@ func NewDisplayMatrixFromRotation(angle float64) *DisplayMatrix {
return m
}
func (m DisplayMatrix) Bytes() []byte {
func (m DisplayMatrix) bytes() []byte {
b := make([]byte, 0, 36)
for _, v := range m {
b = binary.LittleEndian.AppendUint32(b, v)

View File

@@ -7,17 +7,17 @@ import (
)
func TestDisplayMatrix(t *testing.T) {
_, err := NewDisplayMatrixFromBytes([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})
_, err := newDisplayMatrixFromBytes([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})
require.Error(t, err)
b := []byte{0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64}
dm, err := NewDisplayMatrixFromBytes(b)
dm, err := newDisplayMatrixFromBytes(b)
require.NoError(t, err)
require.Equal(t, DisplayMatrix{0x0, 0xffff0000, 0x0, 0x10000, 0x0, 0x0, 0x0, 0x0, 0x40000000}, *dm)
require.Equal(t, -90.0, dm.Rotation())
require.Equal(t, b, dm.Bytes())
require.Equal(t, b, dm.bytes())
dm = NewDisplayMatrixFromRotation(-90)
require.Equal(t, -90.0, dm.Rotation())
dm, err = NewDisplayMatrixFromBytes([]byte{0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64})
dm, err = newDisplayMatrixFromBytes([]byte{0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64})
require.NoError(t, err)
require.Equal(t, DisplayMatrix{0x0, 0x10000, 0x0, 0xffff0000, 0x0, 0x0, 0x0, 0x0, 0x40000000}, *dm)
require.Equal(t, 90.0, dm.Rotation())

View File

@@ -239,14 +239,9 @@ func (f *Frame) SetSampleRate(r int) {
f.c.sample_rate = C.int(r)
}
// https://ffmpeg.org/doxygen/7.0/group__lavu__frame.html#gae05843b941b79e56b955674e581a8262
func (f *Frame) NewSideData(t FrameSideDataType, size uint64) *FrameSideData {
return newFrameSideDataFromC(C.av_frame_new_side_data(f.c, (C.enum_AVFrameSideDataType)(t), C.size_t(size)))
}
// https://ffmpeg.org/doxygen/7.0/group__lavu__frame.html#gadec0efb470b1eead6a979333d9deca0c
func (f *Frame) SideData(t FrameSideDataType) *FrameSideData {
return newFrameSideDataFromC(C.av_frame_get_side_data(f.c, (C.enum_AVFrameSideDataType)(t)))
// https://ffmpeg.org/doxygen/7.0/structAVFrame.html#a44d40e03fe22a0511c9157dab22143ee
func (f *Frame) SideData() *FrameSideData {
return newFrameSideDataFromC(&f.c.side_data, &f.c.nb_side_data)
}
// https://ffmpeg.org/doxygen/7.0/structAVFrame.html#a1e71ce60cedd5f3b6811714a9f7f9e0a

5
frame_side_data.c Normal file
View File

@@ -0,0 +1,5 @@
#include <libavutil/frame.h>
AVRegionOfInterest* astiavConvertRegionsOfInterestFrameSideData(AVFrameSideData *sd) {
return (AVRegionOfInterest*)sd->data;
}

View File

@@ -1,38 +1,79 @@
package astiav
//#include <libavutil/frame.h>
//#include "frame_side_data.h"
import "C"
import (
"errors"
"math"
"unsafe"
)
// https://ffmpeg.org/doxygen/7.0/structAVFrameSideData.html
// https://ffmpeg.org/doxygen/7.0/group__lavu__frame.html#gae01fa7e427274293aacdf2adc17076bc
type FrameSideData struct {
c *C.AVFrameSideData
sd ***C.AVFrameSideData
size *C.int
}
func newFrameSideDataFromC(c *C.AVFrameSideData) *FrameSideData {
if c == nil {
return nil
func newFrameSideDataFromC(sd ***C.AVFrameSideData, size *C.int) *FrameSideData {
return &FrameSideData{
sd: sd,
size: size,
}
return &FrameSideData{c: c}
}
// https://ffmpeg.org/doxygen/7.0/structAVFrameSideData.html#a76937ad48652a5a0cc4bff65fc6c886e
func (d *FrameSideData) Data() []byte {
return bytesFromC(func(size *C.size_t) *C.uint8_t {
*size = d.c.size
return d.c.data
})
// https://ffmpeg.org/doxygen/7.0/group__lavu__frame.html#ggae01fa7e427274293aacdf2adc17076bcaf525ec92d2c5a78d44950bc3f29972aa
func (d *FrameSideData) RegionsOfInterest() *frameSideDataRegionsOfInterest {
return newFrameSideDataRegionsOfInterest(d)
}
// https://ffmpeg.org/doxygen/7.0/structAVFrameSideData.html#a76937ad48652a5a0cc4bff65fc6c886e
func (d *FrameSideData) SetData(b []byte) {
C.memcpy(unsafe.Pointer(d.c.data), unsafe.Pointer(&b[0]), C.size_t(math.Min(float64(len(b)), float64(d.c.size))))
type frameSideDataRegionsOfInterest struct {
d *FrameSideData
}
// https://ffmpeg.org/doxygen/7.0/structAVFrameSideData.html#a07ff3499827c124591ff4bae6f68eec0
func (d *FrameSideData) Type() FrameSideDataType {
return FrameSideDataType(d.c._type)
func newFrameSideDataRegionsOfInterest(d *FrameSideData) *frameSideDataRegionsOfInterest {
return &frameSideDataRegionsOfInterest{d: d}
}
func (d *frameSideDataRegionsOfInterest) data(sd *C.AVFrameSideData) *[(math.MaxInt32 - 1) / C.sizeof_AVRegionOfInterest]C.AVRegionOfInterest {
return (*[(math.MaxInt32 - 1) / C.sizeof_AVRegionOfInterest](C.AVRegionOfInterest))(unsafe.Pointer(C.astiavConvertRegionsOfInterestFrameSideData(sd)))
}
func (d *frameSideDataRegionsOfInterest) Add(rois []RegionOfInterest) error {
sd := C.av_frame_side_data_new(d.d.sd, d.d.size, C.AV_FRAME_DATA_REGIONS_OF_INTEREST, C.size_t(C.sizeof_AVRegionOfInterest*len(rois)), 0)
if sd == nil {
return errors.New("astiav: nil pointer")
}
crois := d.data(sd)
for i, roi := range rois {
crois[i].bottom = C.int(roi.Bottom)
crois[i].left = C.int(roi.Left)
crois[i].qoffset = roi.QuantisationOffset.c
crois[i].right = C.int(roi.Right)
crois[i].self_size = C.sizeof_AVRegionOfInterest
crois[i].top = C.int(roi.Top)
}
return nil
}
func (d *frameSideDataRegionsOfInterest) Get() ([]RegionOfInterest, error) {
sd := C.av_frame_side_data_get(*d.d.sd, *d.d.size, C.AV_FRAME_DATA_REGIONS_OF_INTEREST)
if sd == nil {
return nil, nil
}
crois := d.data(sd)
rois := make([]RegionOfInterest, int(sd.size/C.sizeof_AVRegionOfInterest))
for i := range rois {
rois[i] = RegionOfInterest{
Bottom: int(crois[i].bottom),
Left: int(crois[i].left),
QuantisationOffset: newRationalFromC(crois[i].qoffset),
Right: int(crois[i].right),
Top: int(crois[i].top),
}
}
return rois, nil
}

3
frame_side_data.h Normal file
View File

@@ -0,0 +1,3 @@
#include <libavutil/frame.h>
AVRegionOfInterest* astiavConvertRegionsOfInterestFrameSideData(AVFrameSideData *sd);

61
frame_side_data_test.go Normal file
View File

@@ -0,0 +1,61 @@
package astiav
import (
"testing"
"github.com/stretchr/testify/require"
)
func testFrameSideData(sd *FrameSideData, t *testing.T) {
rois1 := []RegionOfInterest{
{
Bottom: 1,
Left: 2,
QuantisationOffset: NewRational(3, 4),
Right: 5,
Top: 6,
},
{
Bottom: 7,
Left: 8,
QuantisationOffset: NewRational(9, 10),
Right: 11,
Top: 12,
},
}
require.NoError(t, sd.RegionsOfInterest().Add(rois1))
rois2, err := sd.RegionsOfInterest().Get()
require.NoError(t, err)
require.Equal(t, rois1, rois2)
}
func TestFrameSideData(t *testing.T) {
f := AllocFrame()
require.NotNil(t, f)
defer f.Free()
sd := f.SideData()
rois1, err := sd.RegionsOfInterest().Get()
require.NoError(t, err)
require.Nil(t, rois1)
rois1 = []RegionOfInterest{
{
Bottom: 1,
Left: 2,
QuantisationOffset: NewRational(3, 4),
Right: 5,
Top: 6,
},
{
Bottom: 7,
Left: 8,
QuantisationOffset: NewRational(9, 10),
Right: 11,
Top: 12,
},
}
require.NoError(t, sd.RegionsOfInterest().Add(rois1))
rois2, err := sd.RegionsOfInterest().Get()
require.NoError(t, err)
require.Equal(t, rois1, rois2)
}

View File

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

View File

@@ -1,7 +1,6 @@
package astiav
import (
"bytes"
"testing"
"unsafe"
@@ -90,18 +89,7 @@ func TestFrame(t *testing.T) {
f5 := AllocFrame()
require.NotNil(t, f5)
defer f5.Free()
sd := f5.NewSideData(FrameSideDataTypeAudioServiceType, 4)
require.NotNil(t, sd)
sd.SetData([]byte{1, 2, 3})
sd = f5.SideData(FrameSideDataTypeAudioServiceType)
require.NotNil(t, sd)
require.Equal(t, FrameSideDataTypeAudioServiceType, sd.Type())
require.True(t, bytes.HasPrefix(sd.Data(), []byte{1, 2, 3}))
require.Len(t, sd.Data(), 4)
sd.SetData([]byte{1, 2, 3, 4, 5})
sd = f5.SideData(FrameSideDataTypeAudioServiceType)
require.NotNil(t, sd)
require.Equal(t, []byte{1, 2, 3, 4}, sd.Data())
testFrameSideData(f5.SideData(), t)
f6 := AllocFrame()
require.NotNil(t, f6)

View File

@@ -1,8 +0,0 @@
#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);
}

View File

@@ -1,21 +0,0 @@
package astiav
//#include "int_read_write.h"
import "C"
import "unsafe"
// https://ffmpeg.org/doxygen/7.0/avr32_2intreadwrite_8h.html#ace46e41b9bd6cac88fb7109ffd657f9a
func RL32(i []byte) uint32 {
if len(i) == 0 {
return 0
}
return uint32(C.astiavRL32((*C.uint8_t)(unsafe.Pointer(&i[0]))))
}
// https://ffmpeg.org/doxygen/7.0/avr32_2intreadwrite_8h.html#ace46e41b9bd6cac88fb7109ffd657f9a
func RL32WithOffset(i []byte, offset uint) uint32 {
if len(i) == 0 {
return 0
}
return uint32(C.astiavRL32WithOffset((*C.uint8_t)(unsafe.Pointer(&i[0])), C.int(offset)))
}

View File

@@ -1,4 +0,0 @@
#include <stdint.h>
uint32_t astiavRL32(uint8_t *i);
uint32_t astiavRL32WithOffset(uint8_t *i, int o);

View File

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

View File

@@ -8,6 +8,7 @@ import (
)
// https://ffmpeg.org/doxygen/7.0/structAVPacketSideData.html
// https://ffmpeg.org/doxygen/7.0/group__lavc__packet__side__data.html#ga9a80bfcacc586b483a973272800edb97
type PacketSideData struct {
sd **C.AVPacketSideData
size *C.int
@@ -20,13 +21,63 @@ func newPacketSideDataFromC(sd **C.AVPacketSideData, size *C.int) *PacketSideDat
}
}
// https://ffmpeg.org/doxygen/7.0/group__lavc__packet__side__data.html#gga9a80bfcacc586b483a973272800edb97aab8c149a1e6c67aad340733becec87e1
func (d *PacketSideData) DisplayMatrix() *packetSideDataDisplayMatrix {
return newPacketSideDataDisplayMatrix(d)
}
type packetSideDataDisplayMatrix struct {
d *PacketSideData
}
func newPacketSideDataDisplayMatrix(d *PacketSideData) *packetSideDataDisplayMatrix {
return &packetSideDataDisplayMatrix{d: d}
}
func (d *packetSideDataDisplayMatrix) Add(m *DisplayMatrix) error {
return d.d.addBytes(C.AV_PKT_DATA_DISPLAYMATRIX, m.bytes())
}
func (d *packetSideDataDisplayMatrix) Get() (*DisplayMatrix, error) {
b := d.d.getBytes(C.AV_PKT_DATA_DISPLAYMATRIX)
if len(b) == 0 {
return nil, nil
}
return newDisplayMatrixFromBytes(b)
}
// https://ffmpeg.org/doxygen/7.0/group__lavc__packet__side__data.html#gga9a80bfcacc586b483a973272800edb97a2093332d8086d25a04942ede61007f6a
func (d *PacketSideData) SkipSamples() *packetSideDataSkipSamples {
return newPacketSideDataSkipSamples(d)
}
type packetSideDataSkipSamples struct {
d *PacketSideData
}
func newPacketSideDataSkipSamples(d *PacketSideData) *packetSideDataSkipSamples {
return &packetSideDataSkipSamples{d: d}
}
func (d *packetSideDataSkipSamples) Add(ss *SkipSamples) error {
return d.d.addBytes(C.AV_PKT_DATA_SKIP_SAMPLES, ss.bytes())
}
func (d *packetSideDataSkipSamples) Get() (*SkipSamples, error) {
b := d.d.getBytes(C.AV_PKT_DATA_SKIP_SAMPLES)
if len(b) == 0 {
return nil, nil
}
return newSkipSamplesFromBytes(b)
}
// https://ffmpeg.org/doxygen/7.0/group__lavc__packet__side__data.html#gad208a666db035802403ea994912a83db
func (d *PacketSideData) Add(t PacketSideDataType, b []byte) error {
func (d *PacketSideData) addBytes(t C.enum_AVPacketSideDataType, b []byte) error {
if len(b) == 0 {
return nil
}
sd := C.av_packet_side_data_new(d.sd, d.size, (C.enum_AVPacketSideDataType)(t), C.size_t(len(b)), 0)
sd := C.av_packet_side_data_new(d.sd, d.size, t, C.size_t(len(b)), 0)
if sd == nil {
return errors.New("astiav: nil pointer")
}
@@ -36,12 +87,12 @@ func (d *PacketSideData) Add(t PacketSideDataType, b []byte) error {
}
// https://ffmpeg.org/doxygen/7.0/group__lavc__packet__side__data.html#ga61a3a0fba92a308208c8ab957472d23c
func (d *PacketSideData) Get(t PacketSideDataType) []byte {
func (d *PacketSideData) getBytes(t C.enum_AVPacketSideDataType) []byte {
return bytesFromC(func(size *C.size_t) *C.uint8_t {
if d.sd == nil || d.size == nil {
return nil
}
sd := C.av_packet_side_data_get(*d.sd, *d.size, (C.enum_AVPacketSideDataType)(t))
sd := C.av_packet_side_data_get(*d.sd, *d.size, t)
if sd == nil {
return nil
}

View File

@@ -6,11 +6,39 @@ import (
"github.com/stretchr/testify/require"
)
func testPacketSideData(sd *PacketSideData, t *testing.T) {
m1 := NewDisplayMatrixFromRotation(90)
require.NoError(t, sd.DisplayMatrix().Add(m1))
m2, err := sd.DisplayMatrix().Get()
require.NoError(t, err)
require.Equal(t, m1.Rotation(), m2.Rotation())
}
func TestPacketSideData(t *testing.T) {
cp := AllocCodecParameters()
defer cp.Free()
b := []byte("test")
sd := cp.SideData()
require.NoError(t, sd.Add(PacketSideDataTypeDisplaymatrix, b))
require.Equal(t, b, sd.Get(PacketSideDataTypeDisplaymatrix))
m1, err := sd.DisplayMatrix().Get()
require.NoError(t, err)
require.Nil(t, m1)
m1 = NewDisplayMatrixFromRotation(90)
require.NoError(t, sd.DisplayMatrix().Add(m1))
m2, err := sd.DisplayMatrix().Get()
require.NoError(t, err)
require.Equal(t, m1.Rotation(), m2.Rotation())
ss1, err := sd.SkipSamples().Get()
require.NoError(t, err)
require.Nil(t, ss1)
ss1 = &SkipSamples{
ReasonEnd: 1,
ReasonStart: 2,
SkipEnd: 3,
SkipStart: 4,
}
require.NoError(t, sd.SkipSamples().Add(ss1))
ss2, err := sd.SkipSamples().Get()
require.NoError(t, err)
require.Equal(t, ss1, ss2)
}

View File

@@ -1,47 +0,0 @@
package astiav
//#include <libavcodec/avcodec.h>
import "C"
// https://ffmpeg.org/doxygen/7.0/group__lavc__packet__side__data.html#ga9a80bfcacc586b483a973272800edb97
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)
)
// https://ffmpeg.org/doxygen/7.0/group__lavc__packet__side__data.html#ga78c05e43a5d021eb10e63b28a541bce3
func (t PacketSideDataType) Name() string {
return C.GoString(C.av_packet_side_data_name((C.enum_AVPacketSideDataType)(t)))
}
func (t PacketSideDataType) String() string {
return t.Name()
}

View File

@@ -1,12 +0,0 @@
package astiav
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestPacketSideDataType(t *testing.T) {
require.Equal(t, "Display Matrix", PacketSideDataTypeDisplaymatrix.Name())
require.Equal(t, "Display Matrix", PacketSideDataTypeDisplaymatrix.String())
}

View File

@@ -74,10 +74,7 @@ func TestPacket(t *testing.T) {
pkt5 := AllocPacket()
require.NotNil(t, pkt5)
defer pkt5.Free()
b = []byte{1, 2, 3, 4}
sd := pkt5.SideData()
require.NoError(t, sd.Add(PacketSideDataTypeAudioServiceType, b))
require.Equal(t, b, sd.Get(PacketSideDataTypeAudioServiceType))
testPacketSideData(pkt5.SideData(), t)
pkt6 := AllocPacket()
require.NotNil(t, pkt6)

13
region_of_interest.go Normal file
View File

@@ -0,0 +1,13 @@
package astiav
//#include <libavutil/frame.h>
import "C"
// https://ffmpeg.org/doxygen/7.0/structAVRegionOfInterest.html
type RegionOfInterest struct {
Bottom int
Left int
QuantisationOffset Rational
Right int
Top int
}

34
skip_samples.go Normal file
View File

@@ -0,0 +1,34 @@
package astiav
import (
"encoding/binary"
"fmt"
)
// https://ffmpeg.org/doxygen/7.0/group__lavc__packet__side__data.html#gga9a80bfcacc586b483a973272800edb97a2093332d8086d25a04942ede61007f6a
// https://ffmpeg.org/doxygen/7.0/group__lavu__frame.html#ggae01fa7e427274293aacdf2adc17076bca6b0b1ee4315f322922710f65d02a146b
type SkipSamples struct {
ReasonEnd uint8
ReasonStart uint8
SkipEnd uint32
SkipStart uint32
}
func newSkipSamplesFromBytes(b []byte) (*SkipSamples, error) {
if len(b) < 10 {
return nil, fmt.Errorf("astiav: invalid length %d < 10", len(b))
}
return &SkipSamples{
ReasonEnd: b[9],
ReasonStart: b[8],
SkipEnd: binary.LittleEndian.Uint32(b[4:8]),
SkipStart: binary.LittleEndian.Uint32(b[0:4]),
}, nil
}
func (ss *SkipSamples) bytes() (b []byte) {
b = binary.LittleEndian.AppendUint32(b, ss.SkipStart)
b = binary.LittleEndian.AppendUint32(b, ss.SkipEnd)
b = append(b, ss.ReasonStart, ss.ReasonEnd)
return b
}

23
skip_samples_test.go Normal file
View File

@@ -0,0 +1,23 @@
package astiav
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestSkipSamples(t *testing.T) {
_, err := newSkipSamplesFromBytes([]byte("123456789"))
require.Error(t, err)
ss1 := &SkipSamples{
ReasonEnd: 1,
ReasonStart: 2,
SkipEnd: 3,
SkipStart: 4,
}
b1 := ss1.bytes()
require.Equal(t, []byte{0x4, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x2, 0x1}, b1)
ss2, err := newSkipSamplesFromBytes(b1)
require.NoError(t, err)
require.Equal(t, ss1, ss2)
}