refactor(ffmpeg): cleanup callbacks and increased coverage
This commit is contained in:
parent
5ff3407305
commit
7367cc4750
6 changed files with 86 additions and 61 deletions
|
|
@ -47,19 +47,6 @@ func goPacketSeek(opaque unsafe.Pointer, offset C.int64_t, whence C.int) C.int64
|
||||||
return C.int64_t(n)
|
return C.int64_t(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
//export goInterrupt
|
|
||||||
func goInterrupt(opaque unsafe.Pointer) C.int {
|
|
||||||
if ctx, ok := pointer.Restore(opaque).(*AVContext); ok {
|
|
||||||
select {
|
|
||||||
case <-ctx.context.Done():
|
|
||||||
return 1
|
|
||||||
default:
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
//export goAVLoggingHandler
|
//export goAVLoggingHandler
|
||||||
func goAVLoggingHandler(level C.int, cstr *C.char) {
|
func goAVLoggingHandler(level C.int, cstr *C.char) {
|
||||||
log(AVLogLevel(level), C.GoString(cstr))
|
log(AVLogLevel(level), C.GoString(cstr))
|
||||||
|
|
|
||||||
|
|
@ -39,10 +39,6 @@ int create_format_context(AVFormatContext *fmt_ctx, void* opaque, int flags) {
|
||||||
seeker = goPacketSeek;
|
seeker = goPacketSeek;
|
||||||
seekable = 1;
|
seekable = 1;
|
||||||
}
|
}
|
||||||
if (flags & INTERRUPT_FLAG) {
|
|
||||||
fmt_ctx->interrupt_callback.callback = goInterrupt;
|
|
||||||
fmt_ctx->interrupt_callback.opaque = opaque;
|
|
||||||
}
|
|
||||||
if (!(avio_ctx = avio_alloc_context(avio_buffer, BUFFER_SIZE, write_flag, opaque, reader, NULL, seeker))) {
|
if (!(avio_ctx = avio_alloc_context(avio_buffer, BUFFER_SIZE, write_flag, opaque, reader, NULL, seeker))) {
|
||||||
av_free(avio_buffer);
|
av_free(avio_buffer);
|
||||||
avformat_free_context(fmt_ctx);
|
avformat_free_context(fmt_ctx);
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@ import (
|
||||||
const (
|
const (
|
||||||
readPacketFlag = 1
|
readPacketFlag = 1
|
||||||
seekPacketFlag = 2
|
seekPacketFlag = 2
|
||||||
interruptFlag = 3
|
|
||||||
hasVideo = 1
|
hasVideo = 1
|
||||||
hasAudio = 2
|
hasAudio = 2
|
||||||
)
|
)
|
||||||
|
|
@ -58,9 +57,8 @@ type AVContext struct {
|
||||||
closed bool
|
closed bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadAVContext(ctx context.Context, reader io.Reader, size int64) (*AVContext, error) {
|
func LoadAVContext(reader io.Reader, size int64) (*AVContext, error) {
|
||||||
av := &AVContext{
|
av := &AVContext{
|
||||||
context: ctx,
|
|
||||||
reader: reader,
|
reader: reader,
|
||||||
size: size,
|
size: size,
|
||||||
selectedIndex: -1,
|
selectedIndex: -1,
|
||||||
|
|
@ -68,7 +66,7 @@ func LoadAVContext(ctx context.Context, reader io.Reader, size int64) (*AVContex
|
||||||
if seeker, ok := reader.(io.Seeker); ok {
|
if seeker, ok := reader.(io.Seeker); ok {
|
||||||
av.seeker = seeker
|
av.seeker = seeker
|
||||||
}
|
}
|
||||||
flags := C.int(readPacketFlag | interruptFlag)
|
flags := C.int(readPacketFlag)
|
||||||
if av.seeker != nil {
|
if av.seeker != nil {
|
||||||
flags |= seekPacketFlag
|
flags |= seekPacketFlag
|
||||||
}
|
}
|
||||||
|
|
@ -111,7 +109,7 @@ func (av *AVContext) Export(bands int) (buf []byte, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if bands < 3 || bands > 4 {
|
if bands < 3 || bands > 4 {
|
||||||
bands = 3
|
bands = 4
|
||||||
}
|
}
|
||||||
if err = convertFrameToRGB(av, bands); err != nil {
|
if err = convertFrameToRGB(av, bands); err != nil {
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@
|
||||||
#define BUFFER_SIZE 1 << 12
|
#define BUFFER_SIZE 1 << 12
|
||||||
#define READ_PACKET_FLAG 1
|
#define READ_PACKET_FLAG 1
|
||||||
#define SEEK_PACKET_FLAG 2
|
#define SEEK_PACKET_FLAG 2
|
||||||
#define INTERRUPT_FLAG 3
|
|
||||||
#define HAS_VIDEO_STREAM 1
|
#define HAS_VIDEO_STREAM 1
|
||||||
#define HAS_AUDIO_STREAM 2
|
#define HAS_AUDIO_STREAM 2
|
||||||
#define ERR_TOO_BIG FFERRTAG('H','M','M','M')
|
#define ERR_TOO_BIG FFERRTAG('H','M','M','M')
|
||||||
|
|
@ -70,5 +69,3 @@ void populate_histogram(ThumbContext *thumb_ctx, int n, AVFrame *frame);
|
||||||
extern int goPacketRead(void *opaque, uint8_t *buf, int buf_size);
|
extern int goPacketRead(void *opaque, uint8_t *buf, int buf_size);
|
||||||
|
|
||||||
extern int64_t goPacketSeek(void *opaque, int64_t seek, int whence);
|
extern int64_t goPacketSeek(void *opaque, int64_t seek, int whence);
|
||||||
|
|
||||||
extern int goInterrupt(void *opaque);
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,14 @@
|
||||||
package ffmpeg
|
package ffmpeg
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/cshum/imagor/vips"
|
"github.com/cshum/imagor/vips"
|
||||||
|
"github.com/cshum/imagor/vips/pointer"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -53,29 +54,28 @@ func TestAVContext(t *testing.T) {
|
||||||
require.NoError(t, os.MkdirAll(baseDir+"golden/export", 0755))
|
require.NoError(t, os.MkdirAll(baseDir+"golden/export", 0755))
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
for _, filename := range files {
|
for _, filename := range files {
|
||||||
for _, frame := range []int{-1, 5, 10, 9999, 99999} {
|
for _, n := range []int{-1, 5, 10, 9999, 99999} {
|
||||||
name := filename
|
name := filename
|
||||||
if frame > -1 {
|
if n > -1 {
|
||||||
name = fmt.Sprintf("%s-%d", filename, frame)
|
name = fmt.Sprintf("%s-%d", filename, n)
|
||||||
}
|
}
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
ctx := context.Background()
|
|
||||||
path := baseDir + filename
|
path := baseDir + filename
|
||||||
reader, err := os.Open(path)
|
reader, err := os.Open(path)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
stats, err := os.Stat(path)
|
stats, err := os.Stat(path)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
av, err := LoadAVContext(ctx, reader, stats.Size())
|
av, err := LoadAVContext(reader, stats.Size())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer av.Close()
|
defer av.Close()
|
||||||
if frame == 10 {
|
if n == 10 {
|
||||||
require.NoError(t, av.ProcessFrames(frame))
|
require.NoError(t, av.ProcessFrames(n))
|
||||||
} else {
|
} else {
|
||||||
if frame == 9999 {
|
if n == 9999 {
|
||||||
require.NoError(t, av.ProcessFrames(-1))
|
require.NoError(t, av.ProcessFrames(-1))
|
||||||
}
|
}
|
||||||
if frame > -1 {
|
if n > -1 {
|
||||||
require.NoError(t, av.SelectFrame(frame))
|
require.NoError(t, av.SelectFrame(n))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
meta := av.Metadata()
|
meta := av.Metadata()
|
||||||
|
|
@ -88,8 +88,14 @@ func TestAVContext(t *testing.T) {
|
||||||
require.NoError(t, os.WriteFile(goldenFile, metaBuf, 0666))
|
require.NoError(t, os.WriteFile(goldenFile, metaBuf, 0666))
|
||||||
}
|
}
|
||||||
bands := 4
|
bands := 4
|
||||||
|
if n == 99999 {
|
||||||
|
bands = 999
|
||||||
|
}
|
||||||
buf, err := av.Export(bands)
|
buf, err := av.Export(bands)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
if bands > 4 {
|
||||||
|
bands = 4
|
||||||
|
}
|
||||||
img, err := vips.LoadImageFromMemory(buf, meta.Width, meta.Height, bands)
|
img, err := vips.LoadImageFromMemory(buf, meta.Width, meta.Height, bands)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
buf, err = img.ExportJpeg(nil)
|
buf, err = img.ExportJpeg(nil)
|
||||||
|
|
@ -109,29 +115,70 @@ func TestNoVideo(t *testing.T) {
|
||||||
require.NoError(t, os.MkdirAll(baseDir+"golden/meta", 0755))
|
require.NoError(t, os.MkdirAll(baseDir+"golden/meta", 0755))
|
||||||
require.NoError(t, os.MkdirAll(baseDir+"golden/export", 0755))
|
require.NoError(t, os.MkdirAll(baseDir+"golden/export", 0755))
|
||||||
for _, filename := range noVideo {
|
for _, filename := range noVideo {
|
||||||
t.Run(filename, func(t *testing.T) {
|
for i := 0; i < 2; i++ {
|
||||||
ctx := context.Background()
|
t.Run(fmt.Sprintf("%s-%d", filename, i), func(t *testing.T) {
|
||||||
path := baseDir + filename
|
path := baseDir + filename
|
||||||
reader, err := os.Open(path)
|
reader, err := os.Open(path)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
stats, err := os.Stat(path)
|
stats, err := os.Stat(path)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
av, err := LoadAVContext(ctx, reader, stats.Size())
|
av, err := LoadAVContext(reader, stats.Size())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer av.Close()
|
defer av.Close()
|
||||||
require.Equal(t, ErrDecoderNotFound, av.ProcessFrames(-1))
|
require.Equal(t, ErrDecoderNotFound, av.ProcessFrames(-1))
|
||||||
meta := av.Metadata()
|
meta := av.Metadata()
|
||||||
metaBuf, err := json.Marshal(meta)
|
metaBuf, err := json.Marshal(meta)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
goldenFile := baseDir + "golden/meta/" + filename + ".meta.json"
|
goldenFile := baseDir + "golden/meta/" + filename + ".meta.json"
|
||||||
if curr, err := os.ReadFile(goldenFile); err == nil {
|
if curr, err := os.ReadFile(goldenFile); err == nil {
|
||||||
assert.Equal(t, string(curr), string(metaBuf))
|
assert.Equal(t, string(curr), string(metaBuf))
|
||||||
} else {
|
} else {
|
||||||
require.NoError(t, os.WriteFile(goldenFile, metaBuf, 0666))
|
require.NoError(t, os.WriteFile(goldenFile, metaBuf, 0666))
|
||||||
}
|
}
|
||||||
buf, err := av.Export(3)
|
if i == 0 {
|
||||||
require.Empty(t, buf)
|
buf, err := av.Export(3)
|
||||||
require.Equal(t, ErrDecoderNotFound, err)
|
require.Empty(t, buf)
|
||||||
})
|
assert.Equal(t, ErrDecoderNotFound, err)
|
||||||
|
} else {
|
||||||
|
assert.Equal(t, ErrDecoderNotFound, av.SelectFrame(1))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCorrupted(t *testing.T) {
|
||||||
|
filename := "macabre.mp4"
|
||||||
|
path := baseDir + filename
|
||||||
|
file, err := os.Open(path)
|
||||||
|
require.NoError(t, err)
|
||||||
|
reader := &readCloser{
|
||||||
|
Reader: io.LimitReader(file, 1024),
|
||||||
|
Closer: file,
|
||||||
|
}
|
||||||
|
stats, err := os.Stat(path)
|
||||||
|
require.NoError(t, err)
|
||||||
|
av, err := LoadAVContext(reader, stats.Size())
|
||||||
|
require.Equal(t, ErrInvalidData, err)
|
||||||
|
require.Empty(t, av)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCorruptedOpaque(t *testing.T) {
|
||||||
|
filename := "macabre.mp4"
|
||||||
|
path := baseDir + filename
|
||||||
|
reader, err := os.Open(path)
|
||||||
|
require.NoError(t, err)
|
||||||
|
stats, err := os.Stat(path)
|
||||||
|
require.NoError(t, err)
|
||||||
|
av, err := LoadAVContext(reader, stats.Size())
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer av.Close()
|
||||||
|
pointer.Unref(av.opaque)
|
||||||
|
err = av.ProcessFrames(-1)
|
||||||
|
assert.Equal(t, ErrUnknown, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
type readCloser struct {
|
||||||
|
io.Reader
|
||||||
|
io.Closer
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -108,7 +108,7 @@ func (p *Processor) Process(ctx context.Context, in *imagor.Blob, params imagorp
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
av, err := ffmpeg.LoadAVContext(ctx, r, size)
|
av, err := ffmpeg.LoadAVContext(r, size)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue