mirror of
https://github.com/asticode/go-astiav.git
synced 2025-10-05 00:02:45 +08:00
Implementation for SWS scale (#33)
* Draft implementation for SWS scale
* Update go.mod
* Revert "Update go.mod"
This reverts commit 760fb8c427
.
* Renaming to AllocSwsContext and remove ChangeResolution this should handled by users
* update example to use new name and remove ChangeResolution
* Follow scaling example from libav, update readme, improve sws
Change scaling example to an similar libav example
Update readme
Add func to UpdateScalingParameters
Rename AllocSwsContext to SwsGetContext
Using a type for scaling algos/flags
* Fix Errors in scale example
* Review Changes
Now using sws context flags
Restructer sws context and adding ned simpler methods to update the ctx
Update the example
Update the test
* Correctly handle error for buffer alloc in PrepareDestinationFrameForScaling
* Add more getter and setter for sws
Get/Set source w,h,pixfmt
Get/Set dst w,h,pixfmt
Get/Set sws flags
* Adding resolution get/set
* Use CachedContext when updating sws ctx
* Review changes
Reorder flags, and update them
# Update Example
Use renaming function to create sws context
Clean up
Use new framdata image funcs
# Sws scale context
New way to update the context
Use sws_scale_frame instead of sws_scale
Reordering funcs for get and set
# Sws sclate context flag
Add "Flag" for algo name
# Update sws test
* fix fmt error args bug
* Review Changes
Simpfy sws update
Use c.int for sws flags
update test
This commit is contained in:
@@ -27,6 +27,7 @@ Examples are located in the [examples](examples) directory and mirror as much as
|
|||||||
|Filtering|[see](examples/filtering/main.go)|[see](https://github.com/FFmpeg/FFmpeg/blob/n5.1.2/doc/examples/filtering_video.c)
|
|Filtering|[see](examples/filtering/main.go)|[see](https://github.com/FFmpeg/FFmpeg/blob/n5.1.2/doc/examples/filtering_video.c)
|
||||||
|Hardware Decoding|[see](examples/hardware_decoding/main.go)|[see](https://github.com/FFmpeg/FFmpeg/blob/n5.1.2/doc/examples/hw_decode.c)
|
|Hardware Decoding|[see](examples/hardware_decoding/main.go)|[see](https://github.com/FFmpeg/FFmpeg/blob/n5.1.2/doc/examples/hw_decode.c)
|
||||||
|Remuxing|[see](examples/remuxing/main.go)|[see](https://github.com/FFmpeg/FFmpeg/blob/n5.1.2/doc/examples/remuxing.c)
|
|Remuxing|[see](examples/remuxing/main.go)|[see](https://github.com/FFmpeg/FFmpeg/blob/n5.1.2/doc/examples/remuxing.c)
|
||||||
|
|Scaling|[see](examples/scaling/main.go)|[see](https://github.com/FFmpeg/FFmpeg/blob/n5.1.2/doc/examples/scaling_video.c)
|
||||||
|Transcoding|[see](examples/transcoding/main.go)|[see](https://github.com/FFmpeg/FFmpeg/blob/n5.1.2/doc/examples/transcoding.c)
|
|Transcoding|[see](examples/transcoding/main.go)|[see](https://github.com/FFmpeg/FFmpeg/blob/n5.1.2/doc/examples/transcoding.c)
|
||||||
|
|
||||||
*Tip: you can use the video sample located in the `testdata` directory for your tests*
|
*Tip: you can use the video sample located in the `testdata` directory for your tests*
|
||||||
|
98
examples/scaling/main.go
Normal file
98
examples/scaling/main.go
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"image/png"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/asticode/go-astiav"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
output = flag.String("o", "", "the png output path")
|
||||||
|
dstWidth = flag.Int("w", 50, "destination width")
|
||||||
|
dstHeight = flag.Int("h", 50, "destination height")
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Handle ffmpeg logs
|
||||||
|
astiav.SetLogLevel(astiav.LogLevelDebug)
|
||||||
|
astiav.SetLogCallback(func(l astiav.LogLevel, fmt, msg, parent string) {
|
||||||
|
log.Printf("ffmpeg log: %s (level: %d)\n", strings.TrimSpace(msg), l)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Parse flags
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
// Usage
|
||||||
|
if *output == "" || *dstWidth <= 0 || *dstHeight <= 0 {
|
||||||
|
log.Println("Usage: <binary path> -o <output path> -w <output width> -h <output height>")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create destination file
|
||||||
|
dstFile, err := os.Create(*output)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(fmt.Errorf("main: creating %s failed: %w", *output, err))
|
||||||
|
}
|
||||||
|
defer dstFile.Close()
|
||||||
|
|
||||||
|
// Create source frame
|
||||||
|
srcFrame := astiav.AllocFrame()
|
||||||
|
defer srcFrame.Free()
|
||||||
|
srcFrame.SetWidth(320)
|
||||||
|
srcFrame.SetHeight(240)
|
||||||
|
srcFrame.SetPixelFormat(astiav.PixelFormatYuv420P)
|
||||||
|
if err = srcFrame.AllocBuffer(1); err != nil {
|
||||||
|
log.Fatal(fmt.Errorf("main: allocating source frame buffer failed: %w", err))
|
||||||
|
}
|
||||||
|
if err = srcFrame.ImageFillBlack(); err != nil {
|
||||||
|
log.Fatal(fmt.Errorf("main: filling source frame with black image failed: %w", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create destination frame
|
||||||
|
dstFrame := astiav.AllocFrame()
|
||||||
|
defer dstFrame.Free()
|
||||||
|
|
||||||
|
// Create software scale context
|
||||||
|
swsCtx, err := astiav.CreateSoftwareScaleContext(
|
||||||
|
srcFrame.Width(),
|
||||||
|
srcFrame.Height(),
|
||||||
|
srcFrame.PixelFormat(),
|
||||||
|
*dstWidth,
|
||||||
|
*dstHeight,
|
||||||
|
astiav.PixelFormatRgba,
|
||||||
|
astiav.NewSoftwareScaleContextFlags(astiav.SoftwareScaleContextFlagBilinear),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(fmt.Errorf("main: creating software scale context failed: %w", err))
|
||||||
|
}
|
||||||
|
defer swsCtx.Free()
|
||||||
|
|
||||||
|
// Scale frame
|
||||||
|
if err := swsCtx.ScaleFrame(srcFrame, dstFrame); err != nil {
|
||||||
|
log.Fatal(fmt.Errorf("main: scaling frame failed: %w", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Guess destination image format
|
||||||
|
img, err := dstFrame.Data().GuessImageFormat()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(fmt.Errorf("main: guessing destination image format failed: %w", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy frame data to destination image
|
||||||
|
if err = dstFrame.Data().ToImage(img); err != nil {
|
||||||
|
log.Fatal(fmt.Errorf("main: copying frame data to destination image failed: %w", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode to png
|
||||||
|
if err = png.Encode(dstFile, img); err != nil {
|
||||||
|
log.Fatal(fmt.Errorf("main: encoding to png failed: %w", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Success
|
||||||
|
log.Println("success")
|
||||||
|
}
|
60
flags.go
60
flags.go
@@ -145,26 +145,6 @@ func (fs FilterCommandFlags) Del(f FilterCommandFlag) FilterCommandFlags {
|
|||||||
|
|
||||||
func (fs FilterCommandFlags) Has(f FilterCommandFlag) bool { return astikit.BitFlags(fs).Has(uint64(f)) }
|
func (fs FilterCommandFlags) Has(f FilterCommandFlag) bool { return astikit.BitFlags(fs).Has(uint64(f)) }
|
||||||
|
|
||||||
type FormatContextCtxFlags astikit.BitFlags
|
|
||||||
|
|
||||||
func NewFormatContextCtxFlags(fs ...FormatContextCtxFlag) FormatContextCtxFlags {
|
|
||||||
o := FormatContextCtxFlags(0)
|
|
||||||
for _, f := range fs {
|
|
||||||
o = o.Add(f)
|
|
||||||
}
|
|
||||||
return o
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fs FormatContextCtxFlags) Add(f FormatContextCtxFlag) FormatContextCtxFlags {
|
|
||||||
return FormatContextCtxFlags(astikit.BitFlags(fs).Add(uint64(f)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fs FormatContextCtxFlags) Del(f FormatContextCtxFlag) FormatContextCtxFlags {
|
|
||||||
return FormatContextCtxFlags(astikit.BitFlags(fs).Del(uint64(f)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fs FormatContextCtxFlags) Has(f FormatContextCtxFlag) bool { return astikit.BitFlags(fs).Has(uint64(f)) }
|
|
||||||
|
|
||||||
type FormatContextFlags astikit.BitFlags
|
type FormatContextFlags astikit.BitFlags
|
||||||
|
|
||||||
func NewFormatContextFlags(fs ...FormatContextFlag) FormatContextFlags {
|
func NewFormatContextFlags(fs ...FormatContextFlag) FormatContextFlags {
|
||||||
@@ -185,6 +165,26 @@ func (fs FormatContextFlags) Del(f FormatContextFlag) FormatContextFlags {
|
|||||||
|
|
||||||
func (fs FormatContextFlags) Has(f FormatContextFlag) bool { return astikit.BitFlags(fs).Has(uint64(f)) }
|
func (fs FormatContextFlags) Has(f FormatContextFlag) bool { return astikit.BitFlags(fs).Has(uint64(f)) }
|
||||||
|
|
||||||
|
type FormatContextCtxFlags astikit.BitFlags
|
||||||
|
|
||||||
|
func NewFormatContextCtxFlags(fs ...FormatContextCtxFlag) FormatContextCtxFlags {
|
||||||
|
o := FormatContextCtxFlags(0)
|
||||||
|
for _, f := range fs {
|
||||||
|
o = o.Add(f)
|
||||||
|
}
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs FormatContextCtxFlags) Add(f FormatContextCtxFlag) FormatContextCtxFlags {
|
||||||
|
return FormatContextCtxFlags(astikit.BitFlags(fs).Add(uint64(f)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs FormatContextCtxFlags) Del(f FormatContextCtxFlag) FormatContextCtxFlags {
|
||||||
|
return FormatContextCtxFlags(astikit.BitFlags(fs).Del(uint64(f)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs FormatContextCtxFlags) Has(f FormatContextCtxFlag) bool { return astikit.BitFlags(fs).Has(uint64(f)) }
|
||||||
|
|
||||||
type FormatEventFlags astikit.BitFlags
|
type FormatEventFlags astikit.BitFlags
|
||||||
|
|
||||||
func NewFormatEventFlags(fs ...FormatEventFlag) FormatEventFlags {
|
func NewFormatEventFlags(fs ...FormatEventFlag) FormatEventFlags {
|
||||||
@@ -285,6 +285,26 @@ func (fs SeekFlags) Del(f SeekFlag) SeekFlags {
|
|||||||
|
|
||||||
func (fs SeekFlags) Has(f SeekFlag) bool { return astikit.BitFlags(fs).Has(uint64(f)) }
|
func (fs SeekFlags) Has(f SeekFlag) bool { return astikit.BitFlags(fs).Has(uint64(f)) }
|
||||||
|
|
||||||
|
type SoftwareScaleContextFlags astikit.BitFlags
|
||||||
|
|
||||||
|
func NewSoftwareScaleContextFlags(fs ...SoftwareScaleContextFlag) SoftwareScaleContextFlags {
|
||||||
|
o := SoftwareScaleContextFlags(0)
|
||||||
|
for _, f := range fs {
|
||||||
|
o = o.Add(f)
|
||||||
|
}
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs SoftwareScaleContextFlags) Add(f SoftwareScaleContextFlag) SoftwareScaleContextFlags {
|
||||||
|
return SoftwareScaleContextFlags(astikit.BitFlags(fs).Add(uint64(f)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs SoftwareScaleContextFlags) Del(f SoftwareScaleContextFlag) SoftwareScaleContextFlags {
|
||||||
|
return SoftwareScaleContextFlags(astikit.BitFlags(fs).Del(uint64(f)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs SoftwareScaleContextFlags) Has(f SoftwareScaleContextFlag) bool { return astikit.BitFlags(fs).Has(uint64(f)) }
|
||||||
|
|
||||||
type StreamEventFlags astikit.BitFlags
|
type StreamEventFlags astikit.BitFlags
|
||||||
|
|
||||||
func NewStreamEventFlags(fs ...StreamEventFlag) StreamEventFlags {
|
func NewStreamEventFlags(fs ...StreamEventFlag) StreamEventFlags {
|
||||||
|
@@ -70,15 +70,6 @@ func TestFilterCommandFlags(t *testing.T) {
|
|||||||
require.False(t, fs.Has(astiav.FilterCommandFlag(2)))
|
require.False(t, fs.Has(astiav.FilterCommandFlag(2)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFormatContextCtxFlags(t *testing.T) {
|
|
||||||
fs := astiav.NewFormatContextCtxFlags(astiav.FormatContextCtxFlag(1))
|
|
||||||
require.True(t, fs.Has(astiav.FormatContextCtxFlag(1)))
|
|
||||||
fs = fs.Add(astiav.FormatContextCtxFlag(2))
|
|
||||||
require.True(t, fs.Has(astiav.FormatContextCtxFlag(2)))
|
|
||||||
fs = fs.Del(astiav.FormatContextCtxFlag(2))
|
|
||||||
require.False(t, fs.Has(astiav.FormatContextCtxFlag(2)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFormatContextFlags(t *testing.T) {
|
func TestFormatContextFlags(t *testing.T) {
|
||||||
fs := astiav.NewFormatContextFlags(astiav.FormatContextFlag(1))
|
fs := astiav.NewFormatContextFlags(astiav.FormatContextFlag(1))
|
||||||
require.True(t, fs.Has(astiav.FormatContextFlag(1)))
|
require.True(t, fs.Has(astiav.FormatContextFlag(1)))
|
||||||
@@ -88,6 +79,15 @@ func TestFormatContextFlags(t *testing.T) {
|
|||||||
require.False(t, fs.Has(astiav.FormatContextFlag(2)))
|
require.False(t, fs.Has(astiav.FormatContextFlag(2)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFormatContextCtxFlags(t *testing.T) {
|
||||||
|
fs := astiav.NewFormatContextCtxFlags(astiav.FormatContextCtxFlag(1))
|
||||||
|
require.True(t, fs.Has(astiav.FormatContextCtxFlag(1)))
|
||||||
|
fs = fs.Add(astiav.FormatContextCtxFlag(2))
|
||||||
|
require.True(t, fs.Has(astiav.FormatContextCtxFlag(2)))
|
||||||
|
fs = fs.Del(astiav.FormatContextCtxFlag(2))
|
||||||
|
require.False(t, fs.Has(astiav.FormatContextCtxFlag(2)))
|
||||||
|
}
|
||||||
|
|
||||||
func TestFormatEventFlags(t *testing.T) {
|
func TestFormatEventFlags(t *testing.T) {
|
||||||
fs := astiav.NewFormatEventFlags(astiav.FormatEventFlag(1))
|
fs := astiav.NewFormatEventFlags(astiav.FormatEventFlag(1))
|
||||||
require.True(t, fs.Has(astiav.FormatEventFlag(1)))
|
require.True(t, fs.Has(astiav.FormatEventFlag(1)))
|
||||||
@@ -133,6 +133,15 @@ func TestSeekFlags(t *testing.T) {
|
|||||||
require.False(t, fs.Has(astiav.SeekFlag(2)))
|
require.False(t, fs.Has(astiav.SeekFlag(2)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSoftwareScaleContextFlags(t *testing.T) {
|
||||||
|
fs := astiav.NewSoftwareScaleContextFlags(astiav.SoftwareScaleContextFlag(1))
|
||||||
|
require.True(t, fs.Has(astiav.SoftwareScaleContextFlag(1)))
|
||||||
|
fs = fs.Add(astiav.SoftwareScaleContextFlag(2))
|
||||||
|
require.True(t, fs.Has(astiav.SoftwareScaleContextFlag(2)))
|
||||||
|
fs = fs.Del(astiav.SoftwareScaleContextFlag(2))
|
||||||
|
require.False(t, fs.Has(astiav.SoftwareScaleContextFlag(2)))
|
||||||
|
}
|
||||||
|
|
||||||
func TestStreamEventFlags(t *testing.T) {
|
func TestStreamEventFlags(t *testing.T) {
|
||||||
fs := astiav.NewStreamEventFlags(astiav.StreamEventFlag(1))
|
fs := astiav.NewStreamEventFlags(astiav.StreamEventFlag(1))
|
||||||
require.True(t, fs.Has(astiav.StreamEventFlag(1)))
|
require.True(t, fs.Has(astiav.StreamEventFlag(1)))
|
||||||
|
@@ -21,13 +21,14 @@ var list = []listItem{
|
|||||||
{Name: "CodecHardwareConfigMethod"},
|
{Name: "CodecHardwareConfigMethod"},
|
||||||
{Name: "Dictionary"},
|
{Name: "Dictionary"},
|
||||||
{Name: "FilterCommand"},
|
{Name: "FilterCommand"},
|
||||||
{Name: "FormatContextCtx"},
|
|
||||||
{Name: "FormatContext"},
|
{Name: "FormatContext"},
|
||||||
|
{Name: "FormatContextCtx"},
|
||||||
{Name: "FormatEvent"},
|
{Name: "FormatEvent"},
|
||||||
{Name: "IOContext"},
|
{Name: "IOContext"},
|
||||||
{Name: "IOFormat"},
|
{Name: "IOFormat"},
|
||||||
{Name: "Packet"},
|
{Name: "Packet"},
|
||||||
{Name: "Seek"},
|
{Name: "Seek"},
|
||||||
|
{Name: "SoftwareScaleContext"},
|
||||||
{Name: "StreamEvent"},
|
{Name: "StreamEvent"},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
203
software_scale_context.go
Normal file
203
software_scale_context.go
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
package astiav
|
||||||
|
|
||||||
|
//#cgo pkg-config: libswscale
|
||||||
|
//#include <libswscale/swscale.h>
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libswscale/swscale_internal.h#L300
|
||||||
|
type SoftwareScaleContext struct {
|
||||||
|
c *C.struct_SwsContext
|
||||||
|
// We need to store attributes in GO since C attributes are internal and therefore not accessible
|
||||||
|
dstFormat C.enum_AVPixelFormat
|
||||||
|
dstH C.int
|
||||||
|
dstW C.int
|
||||||
|
flags C.int
|
||||||
|
srcFormat C.enum_AVPixelFormat
|
||||||
|
srcH C.int
|
||||||
|
srcW C.int
|
||||||
|
}
|
||||||
|
|
||||||
|
type softwareScaleContextUpdate struct {
|
||||||
|
dstFormat *PixelFormat
|
||||||
|
dstH *int
|
||||||
|
dstW *int
|
||||||
|
flags *SoftwareScaleContextFlags
|
||||||
|
srcFormat *PixelFormat
|
||||||
|
srcH *int
|
||||||
|
srcW *int
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateSoftwareScaleContext(srcW, srcH int, srcFormat PixelFormat, dstW, dstH int, dstFormat PixelFormat, flags SoftwareScaleContextFlags) (*SoftwareScaleContext, error) {
|
||||||
|
ssc := SoftwareScaleContext{
|
||||||
|
dstFormat: C.enum_AVPixelFormat(dstFormat),
|
||||||
|
dstH: C.int(dstH),
|
||||||
|
dstW: C.int(dstW),
|
||||||
|
flags: C.int(flags),
|
||||||
|
srcFormat: C.enum_AVPixelFormat(srcFormat),
|
||||||
|
srcH: C.int(srcH),
|
||||||
|
srcW: C.int(srcW),
|
||||||
|
}
|
||||||
|
|
||||||
|
ssc.c = C.sws_getContext(
|
||||||
|
ssc.srcW,
|
||||||
|
ssc.srcH,
|
||||||
|
ssc.srcFormat,
|
||||||
|
ssc.dstW,
|
||||||
|
ssc.dstH,
|
||||||
|
ssc.dstFormat,
|
||||||
|
C.int(ssc.flags),
|
||||||
|
nil, nil, nil,
|
||||||
|
)
|
||||||
|
if ssc.c == nil {
|
||||||
|
return nil, errors.New("astiav: empty new context")
|
||||||
|
}
|
||||||
|
return &ssc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ssc *SoftwareScaleContext) ScaleFrame(src, dst *Frame) error {
|
||||||
|
return newError(C.sws_scale_frame(ssc.c, dst.c, src.c))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ssc *SoftwareScaleContext) update(u softwareScaleContextUpdate) error {
|
||||||
|
dstW := ssc.dstW
|
||||||
|
if u.dstW != nil {
|
||||||
|
dstW = C.int(*u.dstW)
|
||||||
|
}
|
||||||
|
|
||||||
|
dstH := ssc.dstH
|
||||||
|
if u.dstH != nil {
|
||||||
|
dstH = C.int(*u.dstH)
|
||||||
|
}
|
||||||
|
|
||||||
|
dstFormat := ssc.dstFormat
|
||||||
|
if u.dstFormat != nil {
|
||||||
|
dstFormat = C.enum_AVPixelFormat(*u.dstFormat)
|
||||||
|
}
|
||||||
|
|
||||||
|
srcW := ssc.srcW
|
||||||
|
if u.srcW != nil {
|
||||||
|
srcW = C.int(*u.srcW)
|
||||||
|
}
|
||||||
|
|
||||||
|
srcH := ssc.srcH
|
||||||
|
if u.srcH != nil {
|
||||||
|
srcH = C.int(*u.srcH)
|
||||||
|
}
|
||||||
|
|
||||||
|
srcFormat := ssc.srcFormat
|
||||||
|
if u.srcFormat != nil {
|
||||||
|
srcFormat = C.enum_AVPixelFormat(*u.srcFormat)
|
||||||
|
}
|
||||||
|
|
||||||
|
flags := ssc.flags
|
||||||
|
if u.flags != nil {
|
||||||
|
flags = C.int(*u.flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
c := C.sws_getCachedContext(
|
||||||
|
ssc.c,
|
||||||
|
srcW,
|
||||||
|
srcH,
|
||||||
|
srcFormat,
|
||||||
|
dstW,
|
||||||
|
dstH,
|
||||||
|
dstFormat,
|
||||||
|
flags,
|
||||||
|
nil, nil, nil,
|
||||||
|
)
|
||||||
|
if c == nil {
|
||||||
|
return errors.New("astiav: empty new context")
|
||||||
|
}
|
||||||
|
|
||||||
|
ssc.c = c
|
||||||
|
ssc.dstW = dstW
|
||||||
|
ssc.dstH = dstH
|
||||||
|
ssc.dstFormat = dstFormat
|
||||||
|
ssc.srcW = srcW
|
||||||
|
ssc.srcH = srcH
|
||||||
|
ssc.srcFormat = srcFormat
|
||||||
|
ssc.flags = flags
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ssc *SoftwareScaleContext) Flags() SoftwareScaleContextFlags {
|
||||||
|
return SoftwareScaleContextFlags(ssc.flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ssc *SoftwareScaleContext) SetFlags(swscf SoftwareScaleContextFlags) error {
|
||||||
|
return ssc.update(softwareScaleContextUpdate{flags: &swscf})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ssc *SoftwareScaleContext) DestinationWidth() int {
|
||||||
|
return int(ssc.dstW)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ssc *SoftwareScaleContext) SetDestinationWidth(i int) error {
|
||||||
|
return ssc.update(softwareScaleContextUpdate{dstW: &i})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ssc *SoftwareScaleContext) DestinationHeight() int {
|
||||||
|
return int(ssc.dstH)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ssc *SoftwareScaleContext) SetDestinationHeight(i int) error {
|
||||||
|
return ssc.update(softwareScaleContextUpdate{dstH: &i})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ssc *SoftwareScaleContext) DestinationPixelFormat() PixelFormat {
|
||||||
|
return PixelFormat(ssc.dstFormat)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ssc *SoftwareScaleContext) SetDestinationPixelFormat(p PixelFormat) error {
|
||||||
|
return ssc.update(softwareScaleContextUpdate{dstFormat: &p})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ssc *SoftwareScaleContext) DestinationResolution() (width int, height int) {
|
||||||
|
return int(ssc.dstW), int(ssc.dstH)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ssc *SoftwareScaleContext) SetDestinationResolution(w int, h int) error {
|
||||||
|
return ssc.update(softwareScaleContextUpdate{dstW: &w, dstH: &h})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ssc *SoftwareScaleContext) SourceWidth() int {
|
||||||
|
return int(ssc.srcW)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ssc *SoftwareScaleContext) SetSourceWidth(i int) error {
|
||||||
|
return ssc.update(softwareScaleContextUpdate{srcW: &i})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ssc *SoftwareScaleContext) SourceHeight() int {
|
||||||
|
return int(ssc.srcH)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ssc *SoftwareScaleContext) SetSourceHeight(i int) error {
|
||||||
|
return ssc.update(softwareScaleContextUpdate{srcH: &i})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ssc *SoftwareScaleContext) SourcePixelFormat() PixelFormat {
|
||||||
|
return PixelFormat(ssc.srcFormat)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ssc *SoftwareScaleContext) SetSourcePixelFormat(p PixelFormat) error {
|
||||||
|
return ssc.update(softwareScaleContextUpdate{srcFormat: &p})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ssc *SoftwareScaleContext) SourceResolution() (int, int) {
|
||||||
|
return int(ssc.srcW), int(ssc.srcH)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ssc *SoftwareScaleContext) SetSourceResolution(w int, h int) error {
|
||||||
|
return ssc.update(softwareScaleContextUpdate{srcW: &w, srcH: &h})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ssc *SoftwareScaleContext) Free() {
|
||||||
|
if ssc.c != nil {
|
||||||
|
C.sws_freeContext(ssc.c)
|
||||||
|
}
|
||||||
|
}
|
22
software_scale_context_flag.go
Normal file
22
software_scale_context_flag.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package astiav
|
||||||
|
|
||||||
|
//#cgo pkg-config: libswscale
|
||||||
|
//#include <libswscale/swscale.h>
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
type SoftwareScaleContextFlag int
|
||||||
|
|
||||||
|
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libswscale/swscale.h#L59
|
||||||
|
const (
|
||||||
|
SoftwareScaleContextFlagArea = SoftwareScaleContextFlag(C.SWS_AREA)
|
||||||
|
SoftwareScaleContextFlagBicubic = SoftwareScaleContextFlag(C.SWS_BICUBIC)
|
||||||
|
SoftwareScaleContextFlagBicublin = SoftwareScaleContextFlag(C.SWS_BICUBLIN)
|
||||||
|
SoftwareScaleContextFlagBilinear = SoftwareScaleContextFlag(C.SWS_BILINEAR)
|
||||||
|
SoftwareScaleContextFlagFastBilinear = SoftwareScaleContextFlag(C.SWS_FAST_BILINEAR)
|
||||||
|
SoftwareScaleContextFlagGauss = SoftwareScaleContextFlag(C.SWS_GAUSS)
|
||||||
|
SoftwareScaleContextFlagLanczos = SoftwareScaleContextFlag(C.SWS_LANCZOS)
|
||||||
|
SoftwareScaleContextFlagPoint = SoftwareScaleContextFlag(C.SWS_POINT)
|
||||||
|
SoftwareScaleContextFlagSinc = SoftwareScaleContextFlag(C.SWS_SINC)
|
||||||
|
SoftwareScaleContextFlagSpline = SoftwareScaleContextFlag(C.SWS_SPLINE)
|
||||||
|
SoftwareScaleContextFlagX = SoftwareScaleContextFlag(C.SWS_X)
|
||||||
|
)
|
88
software_scale_context_test.go
Normal file
88
software_scale_context_test.go
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
package astiav_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/asticode/go-astiav"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSoftwareScaleContext(t *testing.T) {
|
||||||
|
f1 := astiav.AllocFrame()
|
||||||
|
require.NotNil(t, f1)
|
||||||
|
defer f1.Free()
|
||||||
|
|
||||||
|
f2 := astiav.AllocFrame()
|
||||||
|
require.NotNil(t, f2)
|
||||||
|
defer f2.Free()
|
||||||
|
|
||||||
|
f3 := astiav.AllocFrame()
|
||||||
|
require.NotNil(t, f3)
|
||||||
|
defer f3.Free()
|
||||||
|
|
||||||
|
srcW := 320
|
||||||
|
srcH := 280
|
||||||
|
srcPixelFormat := astiav.PixelFormatYuv420P
|
||||||
|
dstW := 640
|
||||||
|
dstH := 480
|
||||||
|
dstPixelFormat := astiav.PixelFormatRgba
|
||||||
|
swscf1 := astiav.SoftwareScaleContextFlags(astiav.SoftwareScaleContextFlagBilinear)
|
||||||
|
|
||||||
|
f1.SetHeight(srcH)
|
||||||
|
f1.SetWidth(srcW)
|
||||||
|
f1.SetPixelFormat(srcPixelFormat)
|
||||||
|
require.NoError(t, f1.AllocBuffer(1))
|
||||||
|
|
||||||
|
swsc, err := astiav.CreateSoftwareScaleContext(srcW, srcH, srcPixelFormat, dstW, dstH, dstPixelFormat, swscf1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer swsc.Free()
|
||||||
|
|
||||||
|
require.NoError(t, swsc.ScaleFrame(f1, f2))
|
||||||
|
require.Equal(t, dstH, f2.Height())
|
||||||
|
require.Equal(t, dstW, f2.Width())
|
||||||
|
require.Equal(t, dstPixelFormat, f2.PixelFormat())
|
||||||
|
|
||||||
|
dstW = 1024
|
||||||
|
dstH = 576
|
||||||
|
dstPixelFormat = astiav.PixelFormatYuv420P
|
||||||
|
swscf2 := astiav.SoftwareScaleContextFlags(astiav.SoftwareScaleContextFlagPoint)
|
||||||
|
|
||||||
|
require.Equal(t, swsc.Flags(), swscf1)
|
||||||
|
swsc.SetFlags(swscf2)
|
||||||
|
require.Equal(t, swsc.Flags(), swscf2)
|
||||||
|
|
||||||
|
require.NoError(t, swsc.SetSourceWidth(f2.Width()))
|
||||||
|
require.Equal(t, swsc.SourceWidth(), f2.Width())
|
||||||
|
require.NoError(t, swsc.SetSourceHeight(f2.Height()))
|
||||||
|
require.Equal(t, swsc.SourceHeight(), f2.Height())
|
||||||
|
|
||||||
|
newSourceW := 1280
|
||||||
|
newSourceH := 720
|
||||||
|
require.NoError(t, swsc.SetSourceResolution(newSourceW, newSourceH))
|
||||||
|
w, h := swsc.SourceResolution()
|
||||||
|
require.Equal(t, w, newSourceW)
|
||||||
|
require.Equal(t, h, newSourceH)
|
||||||
|
require.NoError(t, swsc.SetSourceResolution(f2.Width(), f2.Height()))
|
||||||
|
require.NoError(t, swsc.SetSourcePixelFormat(f2.PixelFormat()))
|
||||||
|
require.Equal(t, swsc.SourcePixelFormat(), f2.PixelFormat())
|
||||||
|
|
||||||
|
newDestW := 800
|
||||||
|
newDestH := 600
|
||||||
|
require.NoError(t, swsc.SetDestinationWidth(dstW))
|
||||||
|
require.Equal(t, swsc.DestinationWidth(), dstW)
|
||||||
|
require.NoError(t, swsc.SetDestinationHeight(dstH))
|
||||||
|
require.Equal(t, swsc.DestinationHeight(), dstH)
|
||||||
|
require.NoError(t, swsc.SetDestinationResolution(newDestW, newDestH))
|
||||||
|
w, h = swsc.DestinationResolution()
|
||||||
|
require.Equal(t, w, newDestW)
|
||||||
|
require.Equal(t, h, newDestH)
|
||||||
|
require.NoError(t, swsc.SetDestinationResolution(dstW, dstH))
|
||||||
|
require.NoError(t, swsc.SetDestinationPixelFormat(dstPixelFormat))
|
||||||
|
require.Equal(t, swsc.DestinationPixelFormat(), dstPixelFormat)
|
||||||
|
|
||||||
|
require.NoError(t, swsc.ScaleFrame(f2, f3))
|
||||||
|
require.Equal(t, dstW, f3.Width())
|
||||||
|
require.Equal(t, dstH, f3.Height())
|
||||||
|
require.Equal(t, dstPixelFormat, f3.PixelFormat())
|
||||||
|
|
||||||
|
}
|
Reference in New Issue
Block a user