feat(imagvideo): max_frames(n) filter
* feat: ffmpeg max_frames option in process_frames * test: update golden files
|
|
@ -84,9 +84,9 @@ func LoadAVContext(ctx context.Context, reader io.Reader, size int64) (*AVContex
|
||||||
return av, nil
|
return av, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (av *AVContext) ProcessFrames() (err error) {
|
func (av *AVContext) ProcessFrames(maxFrames int) (err error) {
|
||||||
if av.thumbContext == nil {
|
if av.thumbContext == nil {
|
||||||
return createThumbContext(av)
|
return createThumbContext(av, C.int(maxFrames))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -101,7 +101,7 @@ func (av *AVContext) SelectFrame(n int) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (av *AVContext) Export(bands int) (buf []byte, err error) {
|
func (av *AVContext) Export(bands int) (buf []byte, err error) {
|
||||||
if err = av.ProcessFrames(); err != nil {
|
if err = av.ProcessFrames(-1); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if bands < 3 || bands > 4 {
|
if bands < 3 || bands > 4 {
|
||||||
|
|
@ -245,7 +245,7 @@ func populateHistogram(av *AVContext, frames <-chan *C.AVFrame) <-chan struct{}
|
||||||
return done
|
return done
|
||||||
}
|
}
|
||||||
|
|
||||||
func createThumbContext(av *AVContext) error {
|
func createThumbContext(av *AVContext, maxFrames C.int) error {
|
||||||
pkt := C.create_packet()
|
pkt := C.create_packet()
|
||||||
var frame *C.AVFrame
|
var frame *C.AVFrame
|
||||||
err := C.obtain_next_frame(av.formatContext, av.codecContext, av.stream.index, &pkt, &frame)
|
err := C.obtain_next_frame(av.formatContext, av.codecContext, av.stream.index, &pkt, &frame)
|
||||||
|
|
@ -266,6 +266,9 @@ func createThumbContext(av *AVContext) error {
|
||||||
return avError(err)
|
return avError(err)
|
||||||
}
|
}
|
||||||
n := av.thumbContext.max_frames
|
n := av.thumbContext.max_frames
|
||||||
|
if maxFrames > 0 && n > maxFrames {
|
||||||
|
n = maxFrames
|
||||||
|
}
|
||||||
if av.selectedIndex > -1 && n > av.selectedIndex+1 {
|
if av.selectedIndex > -1 && n > av.selectedIndex+1 {
|
||||||
n = av.selectedIndex + 1
|
n = av.selectedIndex + 1
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ 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, 9999, 99999} {
|
for _, frame := range []int{-1, 5, 10, 9999, 99999} {
|
||||||
name := filename
|
name := filename
|
||||||
if frame > -1 {
|
if frame > -1 {
|
||||||
name = fmt.Sprintf("%s-%d", filename, frame)
|
name = fmt.Sprintf("%s-%d", filename, frame)
|
||||||
|
|
@ -63,14 +63,18 @@ func TestAVContext(t *testing.T) {
|
||||||
av, err := LoadAVContext(ctx, reader, stats.Size())
|
av, err := LoadAVContext(ctx, reader, stats.Size())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer av.Close()
|
defer av.Close()
|
||||||
if frame == 9999 {
|
if frame == 10 {
|
||||||
require.NoError(t, av.ProcessFrames())
|
require.NoError(t, av.ProcessFrames(frame))
|
||||||
}
|
} else {
|
||||||
if frame > -1 {
|
if frame == 9999 {
|
||||||
require.NoError(t, av.SelectFrame(frame))
|
require.NoError(t, av.ProcessFrames(-1))
|
||||||
}
|
}
|
||||||
if frame != 9999 {
|
if frame > -1 {
|
||||||
require.NoError(t, av.ProcessFrames())
|
require.NoError(t, av.SelectFrame(frame))
|
||||||
|
}
|
||||||
|
if frame != 9999 {
|
||||||
|
require.NoError(t, av.ProcessFrames(-1))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
meta := av.Metadata()
|
meta := av.Metadata()
|
||||||
metaBuf, err := json.Marshal(meta)
|
metaBuf, err := json.Marshal(meta)
|
||||||
|
|
|
||||||
|
|
@ -124,8 +124,9 @@ func (p *Processor) Process(ctx context.Context, in *imagor.Blob, params imagorp
|
||||||
bands = 4
|
bands = 4
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "process_frames":
|
case "max_frames":
|
||||||
if err = av.ProcessFrames(); err != nil {
|
n, _ := strconv.Atoi(filter.Args)
|
||||||
|
if err = av.ProcessFrames(n); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
case "frame":
|
case "frame":
|
||||||
|
|
|
||||||
|
|
@ -43,15 +43,17 @@ func TestProcessor(t *testing.T) {
|
||||||
doGoldenTests(t, filepath.Join(testDataDir, "golden/result"), []test{
|
doGoldenTests(t, filepath.Join(testDataDir, "golden/result"), []test{
|
||||||
{name: "mkv", path: "fit-in/100x100/everybody-betray-me.mkv"},
|
{name: "mkv", path: "fit-in/100x100/everybody-betray-me.mkv"},
|
||||||
{name: "mkv specific frame", path: "fit-in/100x100/filters:frame(3)/everybody-betray-me.mkv"},
|
{name: "mkv specific frame", path: "fit-in/100x100/filters:frame(3)/everybody-betray-me.mkv"},
|
||||||
|
{name: "mkv specific max_frames", path: "fit-in/100x100/filters:max_frames(6)/everybody-betray-me.mkv"},
|
||||||
{name: "mkv specific frame exceeded", path: "fit-in/100x100/filters:frame(99999)/everybody-betray-me.mkv"},
|
{name: "mkv specific frame exceeded", path: "fit-in/100x100/filters:frame(99999)/everybody-betray-me.mkv"},
|
||||||
{name: "mkv meta process_frames", path: "meta/filters:process_frames()/everybody-betray-me.mkv"},
|
{name: "mkv meta max_frames", path: "meta/filters:max_frames()/everybody-betray-me.mkv"},
|
||||||
|
{name: "mkv meta max_frames 6", path: "meta/filters:max_frames(6)/everybody-betray-me.mkv"},
|
||||||
{name: "mkv meta", path: "meta/everybody-betray-me.mkv"},
|
{name: "mkv meta", path: "meta/everybody-betray-me.mkv"},
|
||||||
{name: "mp4", path: "200x100/schizo_0.mp4"},
|
{name: "mp4", path: "200x100/schizo_0.mp4"},
|
||||||
{name: "mp4 orient 90", path: "220x100/schizo_90.mp4"},
|
{name: "mp4 orient 90", path: "220x100/schizo_90.mp4"},
|
||||||
{name: "mp4 orient 180", path: "200x100/schizo_180.mp4"},
|
{name: "mp4 orient 180", path: "200x100/schizo_180.mp4"},
|
||||||
{name: "mp4 orient 270", path: "200x100/schizo_270.mp4"},
|
{name: "mp4 orient 270", path: "200x100/schizo_270.mp4"},
|
||||||
{name: "image", path: "fit-in/100x100/demo.png"},
|
{name: "image", path: "fit-in/100x100/demo.png"},
|
||||||
{name: "alpha", path: "fit-in/filters:format(webp)/alpha-webm.webm"},
|
{name: "alpha", path: "fit-in/filters:format(png)/alpha-webm.webm"},
|
||||||
{name: "corrupted", path: "fit-in/100x100/corrupt/everybody-betray-me.mkv", expectCode: 406},
|
{name: "corrupted", path: "fit-in/100x100/corrupt/everybody-betray-me.mkv", expectCode: 406},
|
||||||
}, WithDebug(true), WithLogger(zap.NewExample()))
|
}, WithDebug(true), WithLogger(zap.NewExample()))
|
||||||
doGoldenTests(t, filepath.Join(testDataDir, "golden/result-fallback-image"), []test{
|
doGoldenTests(t, filepath.Join(testDataDir, "golden/result-fallback-image"), []test{
|
||||||
|
|
|
||||||
BIN
testdata/golden/export/alpha-webm.webm-10.jpg
vendored
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
BIN
testdata/golden/export/everybody-betray-me.mkv-10.jpg
vendored
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
testdata/golden/export/macabre.mp4-10.jpg
vendored
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
testdata/golden/export/schizo.flv-10.jpg
vendored
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
testdata/golden/export/schizo_0.mp4-10.jpg
vendored
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
testdata/golden/export/schizo_180.mp4-10.jpg
vendored
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
testdata/golden/export/schizo_270.mp4-10.jpg
vendored
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
testdata/golden/export/schizo_90.mp4-10.jpg
vendored
Normal file
|
After Width: | Height: | Size: 28 KiB |
1
testdata/golden/meta/alpha-webm.webm-10.meta.json
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{"orientation":1,"duration":12040,"width":720,"height":576,"fps":25,"selected_frame":4,"has_video":true,"has_audio":false}
|
||||||
1
testdata/golden/meta/everybody-betray-me.mkv-10.meta.json
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{"orientation":1,"duration":7407,"width":640,"height":480,"fps":30,"selected_frame":5,"has_video":true,"has_audio":true}
|
||||||
1
testdata/golden/meta/macabre.mp4-10.meta.json
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{"orientation":1,"duration":3925,"width":492,"height":360,"fps":30,"selected_frame":4,"has_video":true,"has_audio":true}
|
||||||
1
testdata/golden/meta/schizo.flv-10.meta.json
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{"orientation":1,"duration":2560,"width":480,"height":360,"fps":28,"selected_frame":5,"has_video":true,"has_audio":true}
|
||||||
1
testdata/golden/meta/schizo_0.mp4-10.meta.json
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{"orientation":1,"duration":2544,"width":480,"height":360,"fps":30,"selected_frame":5,"has_video":true,"has_audio":true}
|
||||||
1
testdata/golden/meta/schizo_180.mp4-10.meta.json
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{"orientation":3,"duration":2544,"width":480,"height":360,"fps":30,"selected_frame":4,"has_video":true,"has_audio":true}
|
||||||
1
testdata/golden/meta/schizo_270.mp4-10.meta.json
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{"orientation":6,"duration":2544,"width":360,"height":480,"fps":30,"selected_frame":6,"has_video":true,"has_audio":true}
|
||||||
1
testdata/golden/meta/schizo_90.mp4-10.meta.json
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{"orientation":8,"duration":2544,"width":360,"height":480,"fps":30,"selected_frame":7,"has_video":true,"has_audio":true}
|
||||||
BIN
testdata/golden/result/fit-in/100x100/filters%3Amax_frames%286%29/everybody-betray-me.mkv
vendored
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
testdata/golden/result/fit-in/filters%3Aformat%28png%29/alpha-webm.webm
vendored
Normal file
|
After Width: | Height: | Size: 558 KiB |
|
Before Width: | Height: | Size: 114 KiB |
1
testdata/golden/result/meta/filters%3Amax_frames%286%29/everybody-betray-me.mkv
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{"format":"mkv","content_type":"video/x-matroska","orientation":1,"duration":7407,"width":640,"height":480,"fps":30,"selected_frame":3,"has_video":true,"has_audio":true}
|
||||||