Files
go-astiav/frame_data_test.go
2025-02-08 16:12:34 +01:00

570 lines
12 KiB
Go

package astiav
import (
"fmt"
"image"
"os"
"testing"
"github.com/stretchr/testify/require"
)
type mockedFrameDataFrame struct {
copiedPlanes []frameDataPlane
h int
onBytes func(align int) ([]byte, error)
onPlanes func(b []byte, align int) ([]frameDataPlane, error)
pf PixelFormat
w int
}
var _ frameDataFramer = (*mockedFrameDataFrame)(nil)
func (f *mockedFrameDataFrame) bytes(align int) ([]byte, error) {
return f.onBytes(align)
}
func (f *mockedFrameDataFrame) copyPlanes(ps []frameDataPlane) error {
f.copiedPlanes = ps
return nil
}
func (f *mockedFrameDataFrame) height() int {
return f.h
}
func (f *mockedFrameDataFrame) pixelFormat() PixelFormat {
return f.pf
}
func (f *mockedFrameDataFrame) planes(b []byte, align int) ([]frameDataPlane, error) {
return f.onPlanes(b, align)
}
func (f *mockedFrameDataFrame) width() int {
return f.w
}
func TestFrameDataInternal(t *testing.T) {
fdf := &mockedFrameDataFrame{}
fd := newFrameData(fdf)
for _, v := range []struct {
err bool
i image.Image
pfs []PixelFormat
}{
{
i: &image.Gray{},
pfs: []PixelFormat{PixelFormatGray8},
},
{
i: &image.Gray16{},
pfs: []PixelFormat{PixelFormatGray16Be},
},
{
i: &image.RGBA{},
pfs: []PixelFormat{
PixelFormatRgb0,
PixelFormat0Rgb,
PixelFormatRgb4,
PixelFormatRgb8,
},
},
{
i: &image.NRGBA{},
pfs: []PixelFormat{PixelFormatRgba},
},
{
i: &image.NRGBA64{},
pfs: []PixelFormat{PixelFormatRgba64Be},
},
{
i: &image.NYCbCrA{},
pfs: []PixelFormat{
PixelFormatYuva420P,
PixelFormatYuva422P,
PixelFormatYuva444P,
},
},
{
i: &image.YCbCr{},
pfs: []PixelFormat{
PixelFormatYuv410P,
PixelFormatYuv411P,
PixelFormatYuvj411P,
PixelFormatYuv420P,
PixelFormatYuvj420P,
PixelFormatYuv422P,
PixelFormatYuvj422P,
PixelFormatYuv440P,
PixelFormatYuvj440P,
PixelFormatYuv444P,
PixelFormatYuvj444P,
},
},
{
err: true,
pfs: []PixelFormat{PixelFormatAbgr},
},
} {
for _, pf := range v.pfs {
fdf.pf = pf
i, err := fd.GuessImageFormat()
if v.err {
require.Error(t, err)
} else {
require.IsType(t, v.i, i)
}
}
}
b1 := []byte{0, 1, 2, 3}
fdf.onBytes = func(align int) ([]byte, error) { return b1, nil }
b2, err := fd.Bytes(0)
require.NoError(t, err)
require.Equal(t, b1, b2)
fdf.h = 1
fdf.w = 2
for _, v := range []struct {
e image.Image
i image.Image
pixelFormat PixelFormat
planes []frameDataPlane
}{
{
e: &image.Alpha{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
Rect: image.Rect(0, 0, 2, 1),
},
i: &image.Alpha{},
pixelFormat: PixelFormatRgba,
planes: []frameDataPlane{
{
bytes: []byte{0, 1, 2, 3},
linesize: 1,
},
},
},
{
e: &image.Alpha16{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
Rect: image.Rect(0, 0, 2, 1),
},
i: &image.Alpha16{},
pixelFormat: PixelFormatRgba,
planes: []frameDataPlane{
{
bytes: []byte{0, 1, 2, 3},
linesize: 1,
},
},
},
{
e: &image.CMYK{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
Rect: image.Rect(0, 0, 2, 1),
},
i: &image.CMYK{},
pixelFormat: PixelFormatRgba,
planes: []frameDataPlane{
{
bytes: []byte{0, 1, 2, 3},
linesize: 1,
},
},
},
{
e: &image.Gray{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
Rect: image.Rect(0, 0, 2, 1),
},
i: &image.Gray{},
pixelFormat: PixelFormatRgba,
planes: []frameDataPlane{
{
bytes: []byte{0, 1, 2, 3},
linesize: 1,
},
},
},
{
e: &image.Gray16{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
Rect: image.Rect(0, 0, 2, 1),
},
i: &image.Gray16{},
pixelFormat: PixelFormatRgba,
planes: []frameDataPlane{
{
bytes: []byte{0, 1, 2, 3},
linesize: 1,
},
},
},
{
e: &image.NRGBA{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
Rect: image.Rect(0, 0, 2, 1),
},
i: &image.NRGBA{},
pixelFormat: PixelFormatRgba,
planes: []frameDataPlane{
{
bytes: []byte{0, 1, 2, 3},
linesize: 1,
},
},
},
{
e: &image.NRGBA64{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
Rect: image.Rect(0, 0, 2, 1),
},
i: &image.NRGBA64{},
pixelFormat: PixelFormatRgba,
planes: []frameDataPlane{
{
bytes: []byte{0, 1, 2, 3},
linesize: 1,
},
},
},
{
e: &image.NYCbCrA{
A: []byte{6, 7},
AStride: 4,
YCbCr: image.YCbCr{
Y: []byte{0, 1},
Cb: []byte{2, 3},
Cr: []byte{4, 5},
YStride: 1,
CStride: 2,
SubsampleRatio: image.YCbCrSubsampleRatio444,
Rect: image.Rect(0, 0, 2, 1),
},
},
i: &image.NYCbCrA{},
pixelFormat: PixelFormatYuv444P,
planes: []frameDataPlane{
{
bytes: []byte{0, 1},
linesize: 1,
},
{
bytes: []byte{2, 3},
linesize: 2,
},
{
bytes: []byte{4, 5},
linesize: 3,
},
{
bytes: []byte{6, 7},
linesize: 4,
},
},
},
{
e: &image.RGBA{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
Rect: image.Rect(0, 0, 2, 1),
},
i: &image.RGBA{},
pixelFormat: PixelFormatRgba,
planes: []frameDataPlane{
{
bytes: []byte{0, 1, 2, 3},
linesize: 1,
},
},
},
{
e: &image.RGBA64{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
Rect: image.Rect(0, 0, 2, 1),
},
i: &image.RGBA64{},
pixelFormat: PixelFormatRgba,
planes: []frameDataPlane{
{
bytes: []byte{0, 1, 2, 3},
linesize: 1,
},
},
},
{
e: &image.YCbCr{
Y: []byte{0, 1},
Cb: []byte{2, 3},
Cr: []byte{4, 5},
YStride: 1,
CStride: 2,
SubsampleRatio: image.YCbCrSubsampleRatio420,
Rect: image.Rect(0, 0, 2, 1),
},
i: &image.YCbCr{},
pixelFormat: PixelFormatYuv420P,
planes: []frameDataPlane{
{
bytes: []byte{0, 1},
linesize: 1,
},
{
bytes: []byte{2, 3},
linesize: 2,
},
{
bytes: []byte{4, 5},
linesize: 3,
},
},
},
} {
fdf.pf = v.pixelFormat
fdf.onPlanes = func(b []byte, align int) ([]frameDataPlane, error) { return v.planes, nil }
require.NoError(t, fd.ToImage(v.i))
require.Equal(t, v.e, v.i)
}
b1 = []byte{1, 2, 3, 4}
fdf.onPlanes = func(b []byte, align int) ([]frameDataPlane, error) {
return []frameDataPlane{
{
bytes: b1[:2],
linesize: 1,
},
{
bytes: b1[2:],
linesize: 2,
},
}, nil
}
require.NoError(t, fd.SetBytes(b1, 0))
require.Equal(t, []frameDataPlane{
{bytes: b1[:2], linesize: 1},
{bytes: b1[2:], linesize: 2},
}, fdf.copiedPlanes)
for _, v := range []struct {
expectedCopiedPlanes []frameDataPlane
i image.Image
}{
{
expectedCopiedPlanes: []frameDataPlane{{bytes: []byte{0, 1, 2, 3}, linesize: 1}},
i: &image.Alpha{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
},
},
{
expectedCopiedPlanes: []frameDataPlane{{bytes: []byte{0, 1, 2, 3}, linesize: 1}},
i: &image.Alpha16{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
},
},
{
expectedCopiedPlanes: []frameDataPlane{{bytes: []byte{0, 1, 2, 3}, linesize: 1}},
i: &image.CMYK{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
},
},
{
expectedCopiedPlanes: []frameDataPlane{{bytes: []byte{0, 1, 2, 3}, linesize: 1}},
i: &image.Gray{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
},
},
{
expectedCopiedPlanes: []frameDataPlane{{bytes: []byte{0, 1, 2, 3}, linesize: 1}},
i: &image.Gray16{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
},
},
{
expectedCopiedPlanes: []frameDataPlane{{bytes: []byte{0, 1, 2, 3}, linesize: 1}},
i: &image.NRGBA{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
},
},
{
expectedCopiedPlanes: []frameDataPlane{{bytes: []byte{0, 1, 2, 3}, linesize: 1}},
i: &image.NRGBA64{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
},
},
{
expectedCopiedPlanes: []frameDataPlane{
{bytes: []byte{0, 1}, linesize: 1},
{bytes: []byte{2, 3}, linesize: 2},
{bytes: []byte{4, 5}, linesize: 2},
{bytes: []byte{6, 7}, linesize: 4},
},
i: &image.NYCbCrA{
A: []byte{6, 7},
AStride: 4,
YCbCr: image.YCbCr{
Y: []byte{0, 1},
Cb: []byte{2, 3},
Cr: []byte{4, 5},
YStride: 1,
CStride: 2,
},
},
},
{
expectedCopiedPlanes: []frameDataPlane{{bytes: []byte{0, 1, 2, 3}, linesize: 1}},
i: &image.RGBA{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
},
},
{
expectedCopiedPlanes: []frameDataPlane{{bytes: []byte{0, 1, 2, 3}, linesize: 1}},
i: &image.RGBA64{
Pix: []byte{0, 1, 2, 3},
Stride: 1,
},
},
{
expectedCopiedPlanes: []frameDataPlane{
{bytes: []byte{0, 1}, linesize: 1},
{bytes: []byte{2, 3}, linesize: 2},
{bytes: []byte{4, 5}, linesize: 2},
},
i: &image.YCbCr{
Y: []byte{0, 1},
Cb: []byte{2, 3},
Cr: []byte{4, 5},
YStride: 1,
CStride: 2,
},
},
} {
require.NoError(t, fd.FromImage(v.i))
require.Equal(t, v.expectedCopiedPlanes, fdf.copiedPlanes)
}
}
func TestFrameData(t *testing.T) {
for _, v := range []struct {
ext string
ifmt *InputFormat
md MediaType
name string
}{
{
ext: "pcm",
ifmt: FindInputFormat("s16le"),
md: MediaTypeAudio,
name: "audio-s16le",
},
{
ext: "png",
md: MediaTypeVideo,
name: "image-rgba",
},
{
ext: "h264",
md: MediaTypeVideo,
name: "video-yuv420p",
},
} {
f1, err := globalHelper.inputLastFrame(v.name+"."+v.ext, v.md, v.ifmt)
require.NoError(t, err)
fd1 := f1.Data()
b1, err := fd1.Bytes(1)
require.NoError(t, err)
b2 := []byte(fmt.Sprintf("%+v", b1))
b3, err := os.ReadFile("testdata/" + v.name + "-bytes")
require.NoError(t, err)
require.Equal(t, b3, b2)
var i1 image.Image
switch v.md {
case MediaTypeVideo:
i1, err = fd1.GuessImageFormat()
require.NoError(t, err)
require.NoError(t, fd1.ToImage(i1))
b4 := []byte(fmt.Sprintf("%+v", i1))
b5, err := os.ReadFile("testdata/" + v.name + "-struct")
require.NoError(t, err)
require.Equal(t, b5, b4)
}
f2 := AllocFrame()
defer f2.Free()
fd2 := f2.Data()
align := 0
switch v.md {
case MediaTypeAudio:
f2.SetChannelLayout(f1.ChannelLayout())
f2.SetNbSamples(f1.NbSamples())
f2.SetSampleFormat(f1.SampleFormat())
f2.SetSampleRate(f1.SampleRate())
require.NoError(t, f2.AllocBuffer(align))
case MediaTypeVideo:
align = 1
f2.SetHeight(f1.Height())
f2.SetPixelFormat(f1.PixelFormat())
f2.SetWidth(f1.Width())
require.NoError(t, f2.AllocBuffer(align))
}
switch v.md {
case MediaTypeVideo:
require.NoError(t, fd2.FromImage(i1))
b6, err := fd2.Bytes(align)
require.NoError(t, err)
b7 := []byte(fmt.Sprintf("%+v", b6))
require.Equal(t, b3, b7)
}
switch v.md {
case MediaTypeAudio:
require.NoError(t, f2.SamplesFillSilence())
case MediaTypeVideo:
require.NoError(t, f2.ImageFillBlack())
}
require.NoError(t, fd2.SetBytes(b1, align))
b1[0] -= 1
b8, err := fd2.Bytes(align)
require.NoError(t, err)
b9 := []byte(fmt.Sprintf("%+v", b8))
require.Equal(t, b3, b9)
f3 := AllocFrame()
defer f3.Free()
require.NoError(t, f3.Ref(f2))
switch v.md {
case MediaTypeVideo:
require.Error(t, fd2.FromImage(i1))
}
require.Error(t, fd2.SetBytes(b1, align))
f2.MakeWritable()
switch v.md {
case MediaTypeVideo:
require.NoError(t, fd2.FromImage(i1))
}
require.NoError(t, fd2.SetBytes(b1, align))
}
}