Added FrameData

This commit is contained in:
Quentin Renard
2023-09-02 16:40:35 +02:00
parent e24b820d33
commit 63a083bcad
14 changed files with 456 additions and 245 deletions

View File

@@ -2,7 +2,7 @@ name: Test
on: on:
push: push:
branches: [ "master", "test" ] branches: [ "master", "dev" ]
pull_request: pull_request:
branches: [ "master" ] branches: [ "master" ]
@@ -10,6 +10,7 @@ jobs:
test: test:
strategy: strategy:
fail-fast: false
matrix: matrix:
os: [ubuntu-latest, macos-latest, windows-latest] os: [ubuntu-latest, macos-latest, windows-latest]
@@ -41,35 +42,42 @@ jobs:
run: | run: |
echo "FFMPEG_CACHE_PATH=${{ env.FFMPEG_PATH }}" >> $GITHUB_ENV echo "FFMPEG_CACHE_PATH=${{ env.FFMPEG_PATH }}" >> $GITHUB_ENV
- name: Cache windows ffmpeg - name: Load ffmpeg cache
id: cache-ffmpeg id: load-ffmpeg-cache
uses: actions/cache@v3 uses: actions/cache@v3
with: with:
path: ${{ env.FFMPEG_CACHE_PATH }} path: ${{ env.FFMPEG_CACHE_PATH }}
key: ffmpeg-${{ env.FFMPEG_VERSION }}-${{ runner.os }} key: ffmpeg-${{ env.FFMPEG_VERSION }}-${{ runner.os }}
- if: ${{ steps.cache-ffmpeg.outputs.cache-hit != 'true' && runner.os == 'Linux' }} - if: ${{ steps.load-ffmpeg-cache.outputs.cache-hit != 'true' && runner.os == 'Linux' }}
name: Prepare linux ffmpeg install name: Prepare linux ffmpeg install
run: | run: |
sudo apt-get install yasm sudo apt-get install yasm
- if: ${{ steps.cache-ffmpeg.outputs.cache-hit != 'true' && runner.os == 'macOS' }} - if: ${{ steps.load-ffmpeg-cache.outputs.cache-hit != 'true' && runner.os == 'macOS' }}
name: Prepare macos ffmpeg install name: Prepare macos ffmpeg install
run: | run: |
brew install yasm brew install yasm
- if: ${{ steps.cache-ffmpeg.outputs.cache-hit != 'true' && runner.os == 'Windows' }} - if: ${{ steps.load-ffmpeg-cache.outputs.cache-hit != 'true' && runner.os == 'Windows' }}
name: Prepare windows ffmpeg install name: Prepare windows ffmpeg install
run: | run: |
choco install make choco install make
choco install yasm choco install yasm
echo "FFMPEG_POST_CHECKOUT='&& git apply $(cygpath -u ${{ github.WORKSPACE }})/.github/workflows/windows.patch'" >> $env:GITHUB_ENV echo "FFMPEG_POST_CHECKOUT='&& git apply $(cygpath -u ${{ github.WORKSPACE }})/.github/workflows/windows.patch'" >> $env:GITHUB_ENV
- if: ${{ steps.cache-ffmpeg.outputs.cache-hit != 'true' }} - if: ${{ steps.load-ffmpeg-cache.outputs.cache-hit != 'true' }}
name: Install ffmpeg name: Install ffmpeg
run: | run: |
make install-ffmpeg srcPath=${{ env.FFMPEG_PATH }}/src version=${{ env.FFMPEG_VERSION }} postCheckout=${{ env.FFMPEG_POST_CHECKOUT }} make install-ffmpeg srcPath=${{ env.FFMPEG_PATH }}/src version=${{ env.FFMPEG_VERSION }} postCheckout=${{ env.FFMPEG_POST_CHECKOUT }}
- if: ${{ steps.load-ffmpeg-cache.outputs.cache-hit != 'true' }}
name: Save ffmpeg cache
uses: actions/cache/save@v3
with:
path: ${{ env.FFMPEG_CACHE_PATH }}
key: ffmpeg-${{ env.FFMPEG_VERSION }}-${{ runner.os }}
- if: ${{ runner.os == 'Windows' }} - if: ${{ runner.os == 'Windows' }}
name: Set windows environment variables name: Set windows environment variables
run: | run: |

View File

@@ -1,31 +1,242 @@
package astiav_test package astiav_test
import ( import (
"errors"
"fmt"
"os" "os"
"sync"
"testing" "testing"
"github.com/asticode/go-astiav" "github.com/asticode/go-astiav"
"github.com/asticode/go-astikit" "github.com/asticode/go-astikit"
) )
var global = struct { var globalHelper = newHelper()
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) { func TestMain(m *testing.M) {
// Run // Run
m.Run() m.Run()
// Make sure to close closer // Make sure to close global helper
global.closer.Close() globalHelper.close()
// Exit // Exit
os.Exit(0) os.Exit(0)
} }
type helper struct {
closer *astikit.Closer
inputs map[string]*helperInput
m *sync.Mutex // Locks inputs
}
func newHelper() *helper {
return &helper{
closer: astikit.NewCloser(),
inputs: make(map[string]*helperInput),
m: &sync.Mutex{},
}
}
func (h *helper) close() {
h.closer.Close()
}
type helperInput struct {
firstPkt *astiav.Packet
formatContext *astiav.FormatContext
lastFrame *astiav.Frame
}
func (h *helper) inputFormatContext(name string) (fc *astiav.FormatContext, err error) {
h.m.Lock()
i, ok := h.inputs[name]
if ok && i.formatContext != nil {
h.m.Unlock()
return i.formatContext, nil
}
h.m.Unlock()
if fc = astiav.AllocFormatContext(); fc == nil {
err = errors.New("astiav_test: allocated format context is nil")
return
}
h.closer.Add(fc.Free)
if err = fc.OpenInput("testdata/"+name, nil, nil); err != nil {
err = fmt.Errorf("astiav_test: opening input failed: %w", err)
return
}
h.closer.Add(fc.CloseInput)
if err = fc.FindStreamInfo(nil); err != nil {
err = fmt.Errorf("astiav_test: finding stream info failed: %w", err)
return
}
h.m.Lock()
if _, ok := h.inputs[name]; !ok {
h.inputs[name] = &helperInput{}
}
h.inputs[name].formatContext = fc
h.m.Unlock()
return
}
func (h *helper) inputFirstPacket(name string) (pkt *astiav.Packet, err error) {
h.m.Lock()
i, ok := h.inputs[name]
if ok && i.firstPkt != nil {
h.m.Unlock()
return i.firstPkt, nil
}
h.m.Unlock()
var fc *astiav.FormatContext
if fc, err = h.inputFormatContext(name); 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
}
h.closer.Add(pkt.Free)
if err = fc.ReadFrame(pkt); err != nil {
err = fmt.Errorf("astiav_test: reading frame failed: %w", err)
return
}
h.m.Lock()
h.inputs[name].firstPkt = pkt
h.m.Unlock()
return
}
func (h *helper) inputLastFrame(name string, mediaType astiav.MediaType) (f *astiav.Frame, err error) {
h.m.Lock()
i, ok := h.inputs[name]
if ok && i.lastFrame != nil {
h.m.Unlock()
return i.lastFrame, nil
}
h.m.Unlock()
var fc *astiav.FormatContext
if fc, err = h.inputFormatContext(name); 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() != mediaType {
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
}
h.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 = h.inputFirstPacket(name); err != nil {
err = fmt.Errorf("astiav_test: getting input first packet failed: %w", err)
return
}
pkt2 := astiav.AllocPacket()
h.closer.Add(pkt2.Free)
f = astiav.AllocFrame()
h.closer.Add(f.Free)
lastFrame := astiav.AllocFrame()
h.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 len(pkts) == 0 {
if err = f.Ref(lastFrame); err != nil {
err = fmt.Errorf("astiav_test: last refing frame failed: %w", err)
return
}
err = nil
break
}
} else {
err = fmt.Errorf("astiav_test: reading frame failed: %w", err)
return
}
} else {
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{}
}
h.m.Lock()
h.inputs[name].lastFrame = f
h.m.Unlock()
return
}

View File

@@ -8,8 +8,12 @@ import (
) )
func TestCodecContext(t *testing.T) { func TestCodecContext(t *testing.T) {
_, s1, s2, err := videoInputStreams() fc, err := globalHelper.inputFormatContext("video.mp4")
require.NoError(t, err) require.NoError(t, err)
ss := fc.Streams()
require.Len(t, ss, 2)
s1 := ss[0]
s2 := ss[1]
c1 := astiav.FindDecoder(s1.CodecParameters().CodecID()) c1 := astiav.FindDecoder(s1.CodecParameters().CodecID())
require.NotNil(t, c1) require.NotNil(t, c1)

View File

@@ -8,8 +8,12 @@ import (
) )
func TestCodecParameters(t *testing.T) { func TestCodecParameters(t *testing.T) {
_, s1, s2, err := videoInputStreams() fc, err := globalHelper.inputFormatContext("video.mp4")
require.NoError(t, err) require.NoError(t, err)
ss := fc.Streams()
require.Len(t, ss, 2)
s1 := ss[0]
s2 := ss[1]
cp1 := s1.CodecParameters() cp1 := s1.CodecParameters()
require.Equal(t, int64(441324), cp1.BitRate()) require.Equal(t, int64(441324), cp1.BitRate())

View File

@@ -1,43 +1,18 @@
package astiav_test package astiav_test
import ( import (
"errors"
"fmt"
"testing" "testing"
"github.com/asticode/go-astiav" "github.com/asticode/go-astiav"
"github.com/stretchr/testify/require" "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) { func TestFormatContext(t *testing.T) {
fc1, s1, _, err := videoInputStreams() fc1, err := globalHelper.inputFormatContext("video.mp4")
require.NoError(t, err) require.NoError(t, err)
ss := fc1.Streams()
require.Len(t, ss, 2)
s1 := ss[0]
require.Equal(t, int64(607583), fc1.BitRate()) require.Equal(t, int64(607583), fc1.BitRate())
require.Equal(t, astiav.NewFormatContextCtxFlags(0), fc1.CtxFlags()) require.Equal(t, astiav.NewFormatContextCtxFlags(0), fc1.CtxFlags())

View File

@@ -6,6 +6,7 @@ package astiav
//#include <libavutil/imgutils.h> //#include <libavutil/imgutils.h>
//#include <libavutil/samplefmt.h> //#include <libavutil/samplefmt.h>
import "C" import "C"
import "unsafe"
const NumDataPointers = uint(C.AV_NUM_DATA_POINTERS) const NumDataPointers = uint(C.AV_NUM_DATA_POINTERS)
@@ -53,20 +54,8 @@ func (f *Frame) SetColorRange(r ColorRange) {
f.c.color_range = C.enum_AVColorRange(r) f.c.color_range = C.enum_AVColorRange(r)
} }
func (f *Frame) Data() [NumDataPointers][]byte { func (f *Frame) Data() *FrameData {
b := [NumDataPointers][]byte{} return newFrameData(f)
for i := 0; i < int(NumDataPointers); i++ {
b[i] = bytesFromC(func(size *cUlong) *C.uint8_t {
*size = cUlong(f.c.linesize[i])
if f.c.height > 0 {
*size = *size * cUlong(f.c.height)
} else if f.c.channels > 0 {
*size = *size * cUlong(f.c.channels)
}
return f.c.data[i]
})
}
return b
} }
func (f *Frame) Height() int { func (f *Frame) Height() int {
@@ -89,6 +78,22 @@ func (f *Frame) SetKeyFrame(k bool) {
f.c.key_frame = C.int(i) f.c.key_frame = C.int(i)
} }
func (f *Frame) ImageBufferSize(align int) (int, error) {
ret := C.av_image_get_buffer_size((C.enum_AVSampleFormat)(f.c.format), f.c.width, f.c.height, C.int(align))
if ret < 0 {
return 0, newError(ret)
}
return int(ret), nil
}
func (f *Frame) ImageCopyToBuffer(b []byte, align int) (int, error) {
ret := C.av_image_copy_to_buffer((*C.uint8_t)(unsafe.Pointer(&b[0])), C.int(len(b)), &f.c.data[0], &f.c.linesize[0], (C.enum_AVSampleFormat)(f.c.format), f.c.width, f.c.height, C.int(align))
if ret < 0 {
return 0, newError(ret)
}
return int(ret), nil
}
func (f *Frame) ImageFillBlack() error { func (f *Frame) ImageFillBlack() error {
linesize := [NumDataPointers]cLong{} linesize := [NumDataPointers]cLong{}
for i := 0; i < int(NumDataPointers); i++ { for i := 0; i < int(NumDataPointers); i++ {

120
frame_data.go Normal file
View File

@@ -0,0 +1,120 @@
package astiav
//#include <stdint.h>
import "C"
import (
"errors"
"fmt"
"image"
"strings"
)
type FrameData struct {
f *Frame
}
func newFrameData(f *Frame) *FrameData {
return &FrameData{f: f}
}
func (d *FrameData) Bytes(align int) ([]byte, error) {
switch {
// Video
case d.f.Height() > 0 && d.f.Width() > 0:
// Get buffer size
s, err := d.f.ImageBufferSize(align)
if err != nil {
return nil, fmt.Errorf("astiav: getting image buffer size failed: %w", err)
}
// Invalid buffer size
if s == 0 {
return nil, errors.New("astiav: invalid image buffer size")
}
// Create buffer
b := make([]byte, s)
// Copy image to buffer
if _, err = d.f.ImageCopyToBuffer(b, align); err != nil {
return nil, fmt.Errorf("astiav: copying image to buffer failed: %w", err)
}
return b, nil
}
return nil, errors.New("astiav: frame type not implemented")
}
func (d *FrameData) planeData(i int, sizeFunc func(linesize int) int) []byte {
return bytesFromC(func(size *cUlong) *C.uint8_t {
*size = cUlong(sizeFunc(int(d.f.c.linesize[i])))
return d.f.c.data[i]
})
}
func (d *FrameData) imageYCbCrSubsampleRatio() image.YCbCrSubsampleRatio {
name := d.f.PixelFormat().Name()
for s, r := range map[string]image.YCbCrSubsampleRatio{
"410": image.YCbCrSubsampleRatio410,
"411": image.YCbCrSubsampleRatio411,
"420": image.YCbCrSubsampleRatio420,
"422": image.YCbCrSubsampleRatio422,
"440": image.YCbCrSubsampleRatio440,
"444": image.YCbCrSubsampleRatio444,
} {
if strings.Contains(name, s) {
return r
}
}
return image.YCbCrSubsampleRatio444
}
func (d *FrameData) imageNRGBA() *image.NRGBA {
return &image.NRGBA{
Pix: d.planeData(0, func(linesize int) int { return linesize * d.f.Height() }),
Stride: d.f.Linesize()[0],
Rect: image.Rect(0, 0, d.f.Width(), d.f.Height()),
}
}
func (d *FrameData) imageYCbCr() *image.YCbCr {
return &image.YCbCr{
Y: d.planeData(0, func(linesize int) int { return linesize * d.f.Height() }),
Cb: d.planeData(1, func(linesize int) int { return linesize * d.f.Height() }),
Cr: d.planeData(2, func(linesize int) int { return linesize * d.f.Height() }),
YStride: d.f.Linesize()[0],
CStride: d.f.Linesize()[1],
SubsampleRatio: d.imageYCbCrSubsampleRatio(),
Rect: image.Rect(0, 0, d.f.Width(), d.f.Height()),
}
}
func (d *FrameData) imageNYCbCrA() *image.NYCbCrA {
return &image.NYCbCrA{
YCbCr: *d.imageYCbCr(),
A: d.planeData(3, func(linesize int) int { return linesize * d.f.Height() }),
AStride: d.f.Linesize()[3],
}
}
func (d *FrameData) Image() (image.Image, error) {
// Switch on pixel format
switch d.f.PixelFormat() {
// NRGBA
case PixelFormatRgba:
return d.imageNRGBA(), nil
// NYCbCrA
case PixelFormatYuva420P,
PixelFormatYuva422P,
PixelFormatYuva444P:
return d.imageNYCbCrA(), nil
// YCbCr
case PixelFormatYuv410P,
PixelFormatYuv411P, PixelFormatYuvj411P,
PixelFormatYuv420P, PixelFormatYuvj420P,
PixelFormatYuv422P, PixelFormatYuvj422P,
PixelFormatYuv440P, PixelFormatYuvj440P,
PixelFormatYuv444P, PixelFormatYuvj444P:
return d.imageYCbCr(), nil
}
return nil, fmt.Errorf("astiav: %s pixel format not handled by the Go standard image package", d.f.PixelFormat())
}

47
frame_data_test.go Normal file
View File

@@ -0,0 +1,47 @@
package astiav_test
import (
"image/png"
"os"
"testing"
"github.com/asticode/go-astiav"
"github.com/stretchr/testify/require"
)
func TestFrameData(t *testing.T) {
for _, v := range []struct {
ext string
name string
}{
{
ext: "png",
name: "image-rgba",
},
// TODO Find a way to test yuv and yuva even though result seems to change randomly
} {
// We use a closure to ease closing files
func() {
f, err := globalHelper.inputLastFrame(v.name+"."+v.ext, astiav.MediaTypeVideo)
require.NoError(t, err)
fd := f.Data()
b1, err := fd.Bytes(1)
require.NoError(t, err)
b2, err := os.ReadFile("testdata/" + v.name + "-bytes")
require.NoError(t, err)
require.Equal(t, b1, b2)
f1, err := os.Open("testdata/" + v.name + "." + v.ext)
require.NoError(t, err)
defer f1.Close()
i1, err := fd.Image()
require.NoError(t, err)
i2, err := png.Decode(f1)
require.NoError(t, err)
require.Equal(t, i1, i2)
}()
}
}

View File

@@ -2,136 +2,15 @@ package astiav_test
import ( import (
"bytes" "bytes"
"errors"
"fmt"
"os"
"testing" "testing"
"github.com/asticode/go-astiav" "github.com/asticode/go-astiav"
"github.com/stretchr/testify/require" "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) { func TestFrame(t *testing.T) {
f1, err := videoInputLastVideoFrame() f1, err := globalHelper.inputLastFrame("video.mp4", astiav.MediaTypeVideo)
require.NoError(t, err) require.NoError(t, err)
_, err = os.ReadFile("testdata/frame")
require.NoError(t, err)
// TODO Fix in Github action
//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, [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.PktDts())
@@ -216,7 +95,16 @@ func TestFrame(t *testing.T) {
f6.SetHeight(2) f6.SetHeight(2)
f6.SetPixelFormat(astiav.PixelFormatYuv420P) f6.SetPixelFormat(astiav.PixelFormatYuv420P)
f6.SetWidth(4) f6.SetWidth(4)
require.NoError(t, f6.AllocBuffer(1)) const align = 1
require.NoError(t, f6.AllocImage(1)) require.NoError(t, f6.AllocBuffer(align))
require.NoError(t, f6.AllocImage(align))
require.NoError(t, f6.ImageFillBlack()) require.NoError(t, f6.ImageFillBlack())
n, err := f6.ImageBufferSize(align)
require.NoError(t, err)
require.Equal(t, 12, n)
b := make([]byte, n)
n, err = f6.ImageCopyToBuffer(b, align)
require.NoError(t, err)
require.Equal(t, 12, n)
require.Equal(t, []byte{0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x80, 0x80, 0x80, 0x80}, b)
} }

View File

@@ -1,43 +1,14 @@
package astiav_test package astiav_test
import ( import (
"errors"
"fmt"
"testing" "testing"
"github.com/asticode/go-astiav" "github.com/asticode/go-astiav"
"github.com/stretchr/testify/require" "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) { func TestPacket(t *testing.T) {
pkt1, err := videoInputFirstPacket() pkt1, err := globalHelper.inputFirstPacket("video.mp4")
require.NoError(t, err) 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, []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(0), pkt1.Dts())

View File

@@ -1,40 +1,19 @@
package astiav_test package astiav_test
import ( import (
"fmt"
"testing" "testing"
"github.com/asticode/go-astiav" "github.com/asticode/go-astiav"
"github.com/stretchr/testify/require" "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) { func TestStream(t *testing.T) {
_, s1, s2, err := videoInputStreams() fc, err := globalHelper.inputFormatContext("video.mp4")
require.NoError(t, err) require.NoError(t, err)
ss := fc.Streams()
require.Len(t, ss, 2)
s1 := ss[0]
s2 := ss[1]
require.Equal(t, 0, s1.Index()) require.Equal(t, 0, s1.Index())
require.Equal(t, astiav.NewRational(24, 1), s1.AvgFrameRate()) require.Equal(t, astiav.NewRational(24, 1), s1.AvgFrameRate())

1
testdata/frame vendored

File diff suppressed because one or more lines are too long

BIN
testdata/image-rgba-bytes vendored Normal file

Binary file not shown.

BIN
testdata/image-rgba.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB