mirror of
https://github.com/asticode/go-astiav.git
synced 2025-09-26 20:21:15 +08:00
570 lines
12 KiB
Go
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))
|
|
}
|
|
}
|