mirror of
https://github.com/datarhei/core.git
synced 2025-10-06 08:27:08 +08:00
Make ffmpeg skills compareable
This commit is contained in:
@@ -169,7 +169,7 @@ func (m *manager) HTTPChallengeResolver(ctx context.Context, listenAddress strin
|
|||||||
// AcquireCertificates tries to acquire the certificates for the given hostnames synchronously.
|
// AcquireCertificates tries to acquire the certificates for the given hostnames synchronously.
|
||||||
func (m *manager) AcquireCertificates(ctx context.Context, hostnames []string) error {
|
func (m *manager) AcquireCertificates(ctx context.Context, hostnames []string) error {
|
||||||
m.lock.Lock()
|
m.lock.Lock()
|
||||||
added, removed := slices.Diff(hostnames, m.hostnames)
|
added, removed := slices.DiffComparable(hostnames, m.hostnames)
|
||||||
m.lock.Unlock()
|
m.lock.Unlock()
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
@@ -202,7 +202,7 @@ func (m *manager) AcquireCertificates(ctx context.Context, hostnames []string) e
|
|||||||
// ManageCertificates is the same as AcquireCertificates but it does it in the background.
|
// ManageCertificates is the same as AcquireCertificates but it does it in the background.
|
||||||
func (m *manager) ManageCertificates(ctx context.Context, hostnames []string) error {
|
func (m *manager) ManageCertificates(ctx context.Context, hostnames []string) error {
|
||||||
m.lock.Lock()
|
m.lock.Lock()
|
||||||
added, removed := slices.Diff(hostnames, m.hostnames)
|
added, removed := slices.DiffComparable(hostnames, m.hostnames)
|
||||||
m.hostnames = make([]string, len(hostnames))
|
m.hostnames = make([]string, len(hostnames))
|
||||||
copy(m.hostnames, hostnames)
|
copy(m.hostnames, hostnames)
|
||||||
m.lock.Unlock()
|
m.lock.Unlock()
|
||||||
|
57
ffmpeg/skills/data.go
Normal file
57
ffmpeg/skills/data.go
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
package skills
|
||||||
|
|
||||||
|
var ffmpegdata = `ffmpeg version 4.4.1-datarhei Copyright (c) 2000-2021 the FFmpeg developers
|
||||||
|
built with gcc 10.3.1 (Alpine 10.3.1_git20211027) 20211027
|
||||||
|
configuration: --extra-version=datarhei --prefix=/usr --extra-libs='-lpthread -lm -lz -lsupc++ -lstdc++ -lssl -lcrypto -lz -lc -ldl' --enable-nonfree --enable-gpl --enable-version3 --enable-postproc --enable-static --enable-openssl --enable-omx --enable-omx-rpi --enable-mmal --enable-v4l2_m2m --enable-libfreetype --enable-libsrt --enable-libx264 --enable-libx265 --enable-libvpx --enable-libmp3lame --enable-libopus --enable-libvorbis --disable-ffplay --disable-debug --disable-doc --disable-shared
|
||||||
|
libavutil 56. 70.100 / 56. 70.100
|
||||||
|
libavcodec 58.134.100 / 58.134.100
|
||||||
|
libavformat 58. 76.100 / 58. 76.100
|
||||||
|
libavdevice 58. 13.100 / 58. 13.100
|
||||||
|
libavfilter 7.110.100 / 7.110.100
|
||||||
|
libswscale 5. 9.100 / 5. 9.100
|
||||||
|
libswresample 3. 9.100 / 3. 9.100
|
||||||
|
libpostproc 55. 9.100 / 55. 9.100`
|
||||||
|
|
||||||
|
var filterdata = ` ... afirsrc |->A Generate a FIR coefficients audio stream.
|
||||||
|
... anoisesrc |->A Generate a noise audio signal.
|
||||||
|
... anullsrc |->A Null audio source, return empty audio frames.
|
||||||
|
... hilbert |->A Generate a Hilbert transform FIR coefficients.
|
||||||
|
... sinc |->A Generate a sinc kaiser-windowed low-pass, high-pass, band-pass, or band-reject FIR coefficients.
|
||||||
|
... sine |->A Generate sine wave audio signal.
|
||||||
|
... anullsink A->| Do absolutely nothing with the input audio.
|
||||||
|
... addroi V->V Add region of interest to frame.
|
||||||
|
... alphaextract V->N Extract an alpha channel as a grayscale image component.
|
||||||
|
T.. alphamerge VV->V Copy the luma value of the second input into the alpha channel of the first input.`
|
||||||
|
|
||||||
|
var codecdata = ` DEAIL. aac AAC (Advanced Audio Coding) (decoders: aac aac_fixed aac_at ) (encoders: aac aac_at )
|
||||||
|
DEVI.S y41p Uncompressed YUV 4:1:1 12-bit
|
||||||
|
DEV.LS h264 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10 (encoders: libx264 libx264rgb h264_videotoolbox )
|
||||||
|
DEV.L. flv1 FLV / Sorenson Spark / Sorenson H.263 (Flash Video) (decoders: flv ) (encoders: flv )`
|
||||||
|
|
||||||
|
var formatdata = ` DE mpeg MPEG-1 Systems / MPEG program stream
|
||||||
|
E mpeg1video raw MPEG-1 video
|
||||||
|
E mpeg2video raw MPEG-2 video
|
||||||
|
DE mpegts MPEG-TS (MPEG-2 Transport Stream)
|
||||||
|
D mpegtsraw raw MPEG-TS (MPEG-2 Transport Stream)
|
||||||
|
D mpegvideo raw MPEG video`
|
||||||
|
|
||||||
|
var protocoldata = `Input:
|
||||||
|
async
|
||||||
|
bluray
|
||||||
|
cache
|
||||||
|
Output:
|
||||||
|
crypto
|
||||||
|
file
|
||||||
|
ftp
|
||||||
|
gopher`
|
||||||
|
|
||||||
|
var hwacceldata = `Hardware acceleration methods:
|
||||||
|
videotoolbox`
|
||||||
|
|
||||||
|
var v4ldata = `mmal service 16.1 (platform:bcm2835-v4l2):
|
||||||
|
/dev/video0
|
||||||
|
|
||||||
|
Webcam C170: Webcam C170 (usb-3f980000.usb-1.3):
|
||||||
|
/dev/video1
|
||||||
|
|
||||||
|
`
|
@@ -11,6 +11,8 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/datarhei/core/v16/slices"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Codec represents a codec with its availabe encoders and decoders
|
// Codec represents a codec with its availabe encoders and decoders
|
||||||
@@ -21,12 +23,48 @@ type Codec struct {
|
|||||||
Decoders []string
|
Decoders []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a Codec) Equal(b Codec) bool {
|
||||||
|
if a.Id != b.Id {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.Name != b.Name {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !slices.EqualComparableElements(a.Encoders, b.Encoders) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !slices.EqualComparableElements(a.Decoders, b.Decoders) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
type ffCodecs struct {
|
type ffCodecs struct {
|
||||||
Audio []Codec
|
Audio []Codec
|
||||||
Video []Codec
|
Video []Codec
|
||||||
Subtitle []Codec
|
Subtitle []Codec
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a ffCodecs) Equal(b ffCodecs) bool {
|
||||||
|
if !slices.EqualEqualerElements(a.Audio, b.Audio) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !slices.EqualEqualerElements(a.Video, b.Video) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !slices.EqualEqualerElements(a.Subtitle, b.Subtitle) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// HWDevice represents a hardware device (e.g. USB device)
|
// HWDevice represents a hardware device (e.g. USB device)
|
||||||
type HWDevice struct {
|
type HWDevice struct {
|
||||||
Id string
|
Id string
|
||||||
@@ -35,6 +73,26 @@ type HWDevice struct {
|
|||||||
Media string
|
Media string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a HWDevice) Equal(b HWDevice) bool {
|
||||||
|
if a.Id != b.Id {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.Name != b.Name {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.Extra != b.Extra {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.Media != b.Media {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// Device represents a type of device (e.g. V4L2) including connected actual devices
|
// Device represents a type of device (e.g. V4L2) including connected actual devices
|
||||||
type Device struct {
|
type Device struct {
|
||||||
Id string
|
Id string
|
||||||
@@ -42,11 +100,39 @@ type Device struct {
|
|||||||
Devices []HWDevice
|
Devices []HWDevice
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a Device) Equal(b Device) bool {
|
||||||
|
if a.Id != b.Id {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.Name != b.Name {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !slices.EqualEqualerElements(a.Devices, b.Devices) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
type ffDevices struct {
|
type ffDevices struct {
|
||||||
Demuxers []Device
|
Demuxers []Device
|
||||||
Muxers []Device
|
Muxers []Device
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a ffDevices) Equal(b ffDevices) bool {
|
||||||
|
if !slices.EqualEqualerElements(a.Demuxers, b.Demuxers) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !slices.EqualEqualerElements(a.Muxers, b.Muxers) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// Format represents a supported format (e.g. flv)
|
// Format represents a supported format (e.g. flv)
|
||||||
type Format struct {
|
type Format struct {
|
||||||
Id string
|
Id string
|
||||||
@@ -58,6 +144,18 @@ type ffFormats struct {
|
|||||||
Muxers []Format
|
Muxers []Format
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a ffFormats) Equal(b ffFormats) bool {
|
||||||
|
if !slices.EqualComparableElements(a.Demuxers, b.Demuxers) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !slices.EqualComparableElements(a.Muxers, b.Muxers) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// Protocol represents a supported protocol (e.g. rtsp)
|
// Protocol represents a supported protocol (e.g. rtsp)
|
||||||
type Protocol struct {
|
type Protocol struct {
|
||||||
Id string
|
Id string
|
||||||
@@ -69,6 +167,18 @@ type ffProtocols struct {
|
|||||||
Output []Protocol
|
Output []Protocol
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a ffProtocols) Equal(b ffProtocols) bool {
|
||||||
|
if !slices.EqualComparableElements(a.Input, b.Input) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !slices.EqualComparableElements(a.Output, b.Output) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
type HWAccel struct {
|
type HWAccel struct {
|
||||||
Id string
|
Id string
|
||||||
Name string
|
Name string
|
||||||
@@ -94,6 +204,26 @@ type ffmpeg struct {
|
|||||||
Libraries []Library
|
Libraries []Library
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a ffmpeg) Equal(b ffmpeg) bool {
|
||||||
|
if a.Version != b.Version {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.Compiler != b.Compiler {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.Configuration != b.Configuration {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !slices.EqualComparableElements(a.Libraries, b.Libraries) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// Skills are the detected capabilities of a ffmpeg binary
|
// Skills are the detected capabilities of a ffmpeg binary
|
||||||
type Skills struct {
|
type Skills struct {
|
||||||
FFmpeg ffmpeg
|
FFmpeg ffmpeg
|
||||||
@@ -107,6 +237,38 @@ type Skills struct {
|
|||||||
Protocols ffProtocols
|
Protocols ffProtocols
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a Skills) Equal(b Skills) bool {
|
||||||
|
if !a.FFmpeg.Equal(b.FFmpeg) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !slices.EqualComparableElements(a.Filters, b.Filters) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !slices.EqualComparableElements(a.HWAccels, b.HWAccels) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !a.Codecs.Equal(b.Codecs) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !a.Devices.Equal(b.Devices) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !a.Formats.Equal(b.Formats) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !a.Protocols.Equal(b.Protocols) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// New returns all skills that ffmpeg provides
|
// New returns all skills that ffmpeg provides
|
||||||
func New(binary string) (Skills, error) {
|
func New(binary string) (Skills, error) {
|
||||||
c := Skills{}
|
c := Skills{}
|
||||||
@@ -193,7 +355,7 @@ func filters(binary string) []Filter {
|
|||||||
func parseFilters(data []byte) []Filter {
|
func parseFilters(data []byte) []Filter {
|
||||||
filters := []Filter{}
|
filters := []Filter{}
|
||||||
|
|
||||||
re := regexp.MustCompile(`^\s[TSC.]{3} ([0-9A-Za-z_]+)\s+(?:.*?)\s+(.*)?$`)
|
re := regexp.MustCompile(`^\s*[TSC.]{3} ([0-9A-Za-z_]+)\s+(?:.*?)\s+(.*)?$`)
|
||||||
|
|
||||||
scanner := bufio.NewScanner(bytes.NewReader(data))
|
scanner := bufio.NewScanner(bytes.NewReader(data))
|
||||||
scanner.Split(bufio.ScanLines)
|
scanner.Split(bufio.ScanLines)
|
||||||
@@ -227,7 +389,7 @@ func codecs(binary string) ffCodecs {
|
|||||||
func parseCodecs(data []byte) ffCodecs {
|
func parseCodecs(data []byte) ffCodecs {
|
||||||
codecs := ffCodecs{}
|
codecs := ffCodecs{}
|
||||||
|
|
||||||
re := regexp.MustCompile(`^\s([D.])([E.])([VAS]).{3} ([0-9A-Za-z_]+)\s+(.*?)(?:\(decoders:([^\)]+)\))?\s?(?:\(encoders:([^\)]+)\))?$`)
|
re := regexp.MustCompile(`^\s*([D.])([E.])([VAS]).{3} ([0-9A-Za-z_]+)\s+(.*?)(?:\(decoders:([^\)]+)\))?\s?(?:\(encoders:([^\)]+)\))?$`)
|
||||||
|
|
||||||
scanner := bufio.NewScanner(bytes.NewReader(data))
|
scanner := bufio.NewScanner(bytes.NewReader(data))
|
||||||
scanner.Split(bufio.ScanLines)
|
scanner.Split(bufio.ScanLines)
|
||||||
@@ -286,7 +448,7 @@ func formats(binary string) ffFormats {
|
|||||||
func parseFormats(data []byte) ffFormats {
|
func parseFormats(data []byte) ffFormats {
|
||||||
formats := ffFormats{}
|
formats := ffFormats{}
|
||||||
|
|
||||||
re := regexp.MustCompile(`^\s([D ])([E ]) ([0-9A-Za-z_,]+)\s+(.*?)$`)
|
re := regexp.MustCompile(`^\s*([D ])([E ])\s+([0-9A-Za-z_,]+)\s+(.*?)$`)
|
||||||
|
|
||||||
scanner := bufio.NewScanner(bytes.NewReader(data))
|
scanner := bufio.NewScanner(bytes.NewReader(data))
|
||||||
scanner.Split(bufio.ScanLines)
|
scanner.Split(bufio.ScanLines)
|
||||||
@@ -330,7 +492,7 @@ func devices(binary string) ffDevices {
|
|||||||
func parseDevices(data []byte, binary string) ffDevices {
|
func parseDevices(data []byte, binary string) ffDevices {
|
||||||
devices := ffDevices{}
|
devices := ffDevices{}
|
||||||
|
|
||||||
re := regexp.MustCompile(`^\s([D ])([E ]) ([0-9A-Za-z_,]+)\s+(.*?)$`)
|
re := regexp.MustCompile(`^\s*([D ])([E ]) ([0-9A-Za-z_,]+)\s+(.*?)$`)
|
||||||
|
|
||||||
scanner := bufio.NewScanner(bytes.NewReader(data))
|
scanner := bufio.NewScanner(bytes.NewReader(data))
|
||||||
scanner.Split(bufio.ScanLines)
|
scanner.Split(bufio.ScanLines)
|
||||||
|
@@ -1,8 +1,10 @@
|
|||||||
package skills
|
package skills
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/datarhei/core/v16/slices"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -33,6 +35,70 @@ func TestNewInvalidBinary(t *testing.T) {
|
|||||||
require.Empty(t, skills.Protocols.Output)
|
require.Empty(t, skills.Protocols.Output)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEqualEmptySkills(t *testing.T) {
|
||||||
|
s := Skills{}
|
||||||
|
|
||||||
|
ok := s.Equal(s)
|
||||||
|
require.True(t, ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEuqalSkills(t *testing.T) {
|
||||||
|
s1 := Skills{
|
||||||
|
FFmpeg: parseVersion([]byte(ffmpegdata)),
|
||||||
|
Filters: parseFilters([]byte(filterdata)),
|
||||||
|
HWAccels: parseHWAccels([]byte(hwacceldata)),
|
||||||
|
Codecs: parseCodecs([]byte(codecdata)),
|
||||||
|
Devices: ffDevices{},
|
||||||
|
Formats: parseFormats([]byte(formatdata)),
|
||||||
|
Protocols: parseProtocols([]byte(protocoldata)),
|
||||||
|
}
|
||||||
|
|
||||||
|
devices := parseV4LDevices(bytes.NewBuffer(slices.Copy([]byte(v4ldata))))
|
||||||
|
|
||||||
|
s1.Devices.Demuxers = append(s1.Devices.Demuxers, Device{
|
||||||
|
Id: "v4l2",
|
||||||
|
Name: "webcam",
|
||||||
|
Devices: devices,
|
||||||
|
})
|
||||||
|
s1.Devices.Muxers = append(s1.Devices.Muxers, Device{
|
||||||
|
Id: "v4l2",
|
||||||
|
Name: "webcam",
|
||||||
|
Devices: devices,
|
||||||
|
})
|
||||||
|
|
||||||
|
ok := s1.Equal(s1)
|
||||||
|
require.True(t, ok)
|
||||||
|
|
||||||
|
s2 := Skills{
|
||||||
|
FFmpeg: parseVersion([]byte(ffmpegdata)),
|
||||||
|
Filters: parseFilters([]byte(filterdata)),
|
||||||
|
HWAccels: parseHWAccels([]byte(hwacceldata)),
|
||||||
|
Codecs: parseCodecs([]byte(codecdata)),
|
||||||
|
Devices: ffDevices{},
|
||||||
|
Formats: parseFormats([]byte(formatdata)),
|
||||||
|
Protocols: parseProtocols([]byte(protocoldata)),
|
||||||
|
}
|
||||||
|
|
||||||
|
devices = parseV4LDevices(bytes.NewBuffer(slices.Copy([]byte(v4ldata))))
|
||||||
|
|
||||||
|
s2.Devices.Demuxers = append(s2.Devices.Demuxers, Device{
|
||||||
|
Id: "v4l2",
|
||||||
|
Name: "webcam",
|
||||||
|
Devices: devices,
|
||||||
|
})
|
||||||
|
s2.Devices.Muxers = append(s2.Devices.Muxers, Device{
|
||||||
|
Id: "v4l2",
|
||||||
|
Name: "webcam",
|
||||||
|
Devices: devices,
|
||||||
|
})
|
||||||
|
|
||||||
|
ok = s1.Equal(s2)
|
||||||
|
require.True(t, ok)
|
||||||
|
|
||||||
|
ok = s1.Equal(Skills{})
|
||||||
|
require.False(t, ok)
|
||||||
|
}
|
||||||
|
|
||||||
func TestPatchVersion(t *testing.T) {
|
func TestPatchVersion(t *testing.T) {
|
||||||
data := `ffmpeg version 4.3.1 Copyright (c) 2000-2020 the FFmpeg developers
|
data := `ffmpeg version 4.3.1 Copyright (c) 2000-2020 the FFmpeg developers
|
||||||
built with Apple clang version 12.0.0 (clang-1200.0.32.29)
|
built with Apple clang version 12.0.0 (clang-1200.0.32.29)
|
||||||
@@ -162,17 +228,7 @@ func TestMinorVersion(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCustomVersion(t *testing.T) {
|
func TestCustomVersion(t *testing.T) {
|
||||||
data := `ffmpeg version 4.4.1-datarhei Copyright (c) 2000-2021 the FFmpeg developers
|
data := ffmpegdata
|
||||||
built with gcc 10.3.1 (Alpine 10.3.1_git20211027) 20211027
|
|
||||||
configuration: --extra-version=datarhei --prefix=/usr --extra-libs='-lpthread -lm -lz -lsupc++ -lstdc++ -lssl -lcrypto -lz -lc -ldl' --enable-nonfree --enable-gpl --enable-version3 --enable-postproc --enable-static --enable-openssl --enable-omx --enable-omx-rpi --enable-mmal --enable-v4l2_m2m --enable-libfreetype --enable-libsrt --enable-libx264 --enable-libx265 --enable-libvpx --enable-libmp3lame --enable-libopus --enable-libvorbis --disable-ffplay --disable-debug --disable-doc --disable-shared
|
|
||||||
libavutil 56. 70.100 / 56. 70.100
|
|
||||||
libavcodec 58.134.100 / 58.134.100
|
|
||||||
libavformat 58. 76.100 / 58. 76.100
|
|
||||||
libavdevice 58. 13.100 / 58. 13.100
|
|
||||||
libavfilter 7.110.100 / 7.110.100
|
|
||||||
libswscale 5. 9.100 / 5. 9.100
|
|
||||||
libswresample 3. 9.100 / 3. 9.100
|
|
||||||
libpostproc 55. 9.100 / 55. 9.100`
|
|
||||||
|
|
||||||
f := parseVersion([]byte(data))
|
f := parseVersion([]byte(data))
|
||||||
|
|
||||||
@@ -226,16 +282,7 @@ libpostproc 55. 9.100 / 55. 9.100`
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFilters(t *testing.T) {
|
func TestFilters(t *testing.T) {
|
||||||
data := ` ... afirsrc |->A Generate a FIR coefficients audio stream.
|
data := filterdata
|
||||||
... anoisesrc |->A Generate a noise audio signal.
|
|
||||||
... anullsrc |->A Null audio source, return empty audio frames.
|
|
||||||
... hilbert |->A Generate a Hilbert transform FIR coefficients.
|
|
||||||
... sinc |->A Generate a sinc kaiser-windowed low-pass, high-pass, band-pass, or band-reject FIR coefficients.
|
|
||||||
... sine |->A Generate sine wave audio signal.
|
|
||||||
... anullsink A->| Do absolutely nothing with the input audio.
|
|
||||||
... addroi V->V Add region of interest to frame.
|
|
||||||
... alphaextract V->N Extract an alpha channel as a grayscale image component.
|
|
||||||
T.. alphamerge VV->V Copy the luma value of the second input into the alpha channel of the first input.`
|
|
||||||
|
|
||||||
f := parseFilters([]byte(data))
|
f := parseFilters([]byte(data))
|
||||||
|
|
||||||
@@ -284,10 +331,7 @@ func TestFilters(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCodecs(t *testing.T) {
|
func TestCodecs(t *testing.T) {
|
||||||
data := ` DEAIL. aac AAC (Advanced Audio Coding) (decoders: aac aac_fixed aac_at ) (encoders: aac aac_at )
|
data := codecdata
|
||||||
DEVI.S y41p Uncompressed YUV 4:1:1 12-bit
|
|
||||||
DEV.LS h264 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10 (encoders: libx264 libx264rgb h264_videotoolbox )
|
|
||||||
DEV.L. flv1 FLV / Sorenson Spark / Sorenson H.263 (Flash Video) (decoders: flv ) (encoders: flv )`
|
|
||||||
|
|
||||||
c := parseCodecs([]byte(data))
|
c := parseCodecs([]byte(data))
|
||||||
|
|
||||||
@@ -346,12 +390,7 @@ func TestCodecs(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFormats(t *testing.T) {
|
func TestFormats(t *testing.T) {
|
||||||
data := ` DE mpeg MPEG-1 Systems / MPEG program stream
|
data := formatdata
|
||||||
E mpeg1video raw MPEG-1 video
|
|
||||||
E mpeg2video raw MPEG-2 video
|
|
||||||
DE mpegts MPEG-TS (MPEG-2 Transport Stream)
|
|
||||||
D mpegtsraw raw MPEG-TS (MPEG-2 Transport Stream)
|
|
||||||
D mpegvideo raw MPEG video`
|
|
||||||
|
|
||||||
f := parseFormats([]byte(data))
|
f := parseFormats([]byte(data))
|
||||||
|
|
||||||
@@ -396,15 +435,7 @@ func TestFormats(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestProtocols(t *testing.T) {
|
func TestProtocols(t *testing.T) {
|
||||||
data := `Input:
|
data := protocoldata
|
||||||
async
|
|
||||||
bluray
|
|
||||||
cache
|
|
||||||
Output:
|
|
||||||
crypto
|
|
||||||
file
|
|
||||||
ftp
|
|
||||||
gopher`
|
|
||||||
|
|
||||||
p := parseProtocols([]byte(data))
|
p := parseProtocols([]byte(data))
|
||||||
|
|
||||||
@@ -445,8 +476,7 @@ Output:
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestHWAccels(t *testing.T) {
|
func TestHWAccels(t *testing.T) {
|
||||||
data := `Hardware acceleration methods:
|
data := hwacceldata
|
||||||
videotoolbox`
|
|
||||||
|
|
||||||
p := parseHWAccels([]byte(data))
|
p := parseHWAccels([]byte(data))
|
||||||
|
|
||||||
|
@@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/datarhei/core/v16/slices"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -16,15 +17,9 @@ func TestNoV4LDevices(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestV4LDevices(t *testing.T) {
|
func TestV4LDevices(t *testing.T) {
|
||||||
data := bytes.NewBufferString(`mmal service 16.1 (platform:bcm2835-v4l2):
|
data := v4ldata
|
||||||
/dev/video0
|
|
||||||
|
|
||||||
Webcam C170: Webcam C170 (usb-3f980000.usb-1.3):
|
devices := parseV4LDevices(bytes.NewBuffer(slices.Copy([]byte(data))))
|
||||||
/dev/video1
|
|
||||||
|
|
||||||
`)
|
|
||||||
|
|
||||||
devices := parseV4LDevices(data)
|
|
||||||
|
|
||||||
require.Equal(t, []HWDevice{
|
require.Equal(t, []HWDevice{
|
||||||
{
|
{
|
||||||
|
@@ -6,3 +6,17 @@ func Copy[T any](src []T) []T {
|
|||||||
|
|
||||||
return dst
|
return dst
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Cloner[T any] interface {
|
||||||
|
Clone() T
|
||||||
|
}
|
||||||
|
|
||||||
|
func CopyDeep[T any, X Cloner[T]](src []X) []T {
|
||||||
|
dst := make([]T, len(src))
|
||||||
|
|
||||||
|
for i, c := range src {
|
||||||
|
dst[i] = c.Clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
@@ -13,3 +13,15 @@ func TestCopy(t *testing.T) {
|
|||||||
|
|
||||||
require.Equal(t, []string{"a", "b", "c"}, b)
|
require.Equal(t, []string{"a", "b", "c"}, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a String) Clone() String {
|
||||||
|
return String(string(a))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCopyDeep(t *testing.T) {
|
||||||
|
a := []String{"a", "b", "c"}
|
||||||
|
|
||||||
|
b := CopyDeep[String](a)
|
||||||
|
|
||||||
|
require.Equal(t, []String{"a", "b", "c"}, b)
|
||||||
|
}
|
||||||
|
@@ -1,28 +1,81 @@
|
|||||||
package slices
|
package slices
|
||||||
|
|
||||||
// Diff returns a sliceof newly added entries and a slice of removed entries based
|
// DiffComparable diffs two arrays/slices and returns slices of elements that are only in A and only in B.
|
||||||
// the provided slices.
|
// If some element is present multiple times, each instance is counted separately (e.g. if something is 2x in A and
|
||||||
func Diff[T comparable](next, current []T) ([]T, []T) {
|
// 5x in B, it will be 0x in extraA and 3x in extraB). The order of items in both lists is ignored.
|
||||||
added, removed := []T{}, []T{}
|
// Adapted from https://github.com/stretchr/testify/blob/f97607b89807936ac4ff96748d766cf4b9711f78/assert/assertions.go#L1073C21-L1073C21
|
||||||
|
func DiffComparable[T comparable](listA, listB []T) ([]T, []T) {
|
||||||
|
extraA, extraB := []T{}, []T{}
|
||||||
|
|
||||||
currentMap := map[T]struct{}{}
|
aLen := len(listA)
|
||||||
|
bLen := len(listB)
|
||||||
|
|
||||||
for _, name := range current {
|
// Mark indexes in listA that we already used
|
||||||
currentMap[name] = struct{}{}
|
visited := make([]bool, bLen)
|
||||||
|
for i := 0; i < aLen; i++ {
|
||||||
|
element := listA[i]
|
||||||
|
found := false
|
||||||
|
for j := 0; j < bLen; j++ {
|
||||||
|
if visited[j] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if listB[j] == element {
|
||||||
|
visited[j] = true
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
extraA = append(extraA, element)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, name := range next {
|
for j := 0; j < bLen; j++ {
|
||||||
if _, ok := currentMap[name]; ok {
|
if visited[j] {
|
||||||
delete(currentMap, name)
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
extraB = append(extraB, listB[j])
|
||||||
added = append(added, name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for name := range currentMap {
|
return extraA, extraB
|
||||||
removed = append(removed, name)
|
}
|
||||||
}
|
|
||||||
|
// DiffEqualer diffs two arrays/slices where each element implements the Equaler interface and returns slices of
|
||||||
return added, removed
|
// elements that are only in A and only in B. If some element is present multiple times, each instance is counted
|
||||||
|
// separately (e.g. if something is 2x in A and 5x in B, it will be 0x in extraA and 3x in extraB). The order of
|
||||||
|
// items in both lists is ignored.
|
||||||
|
func DiffEqualer[T any, X Equaler[T]](listA []T, listB []X) ([]T, []X) {
|
||||||
|
extraA, extraB := []T{}, []X{}
|
||||||
|
|
||||||
|
aLen := len(listA)
|
||||||
|
bLen := len(listB)
|
||||||
|
|
||||||
|
// Mark indexes in listA that we already used
|
||||||
|
visited := make([]bool, bLen)
|
||||||
|
for i := 0; i < aLen; i++ {
|
||||||
|
element := listA[i]
|
||||||
|
found := false
|
||||||
|
for j := 0; j < bLen; j++ {
|
||||||
|
if visited[j] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if listB[j].Equal(element) {
|
||||||
|
visited[j] = true
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
extraA = append(extraA, element)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for j := 0; j < bLen; j++ {
|
||||||
|
if visited[j] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
extraB = append(extraB, listB[j])
|
||||||
|
}
|
||||||
|
|
||||||
|
return extraA, extraB
|
||||||
}
|
}
|
||||||
|
@@ -6,12 +6,22 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDiff(t *testing.T) {
|
func TestDiffComparable(t *testing.T) {
|
||||||
a := []string{"c", "d", "e", "f"}
|
a := []string{"c", "d", "e", "f"}
|
||||||
b := []string{"a", "b", "c", "d"}
|
b := []string{"a", "a", "b", "c", "d"}
|
||||||
|
|
||||||
added, removed := Diff(a, b)
|
added, removed := DiffComparable(a, b)
|
||||||
|
|
||||||
require.ElementsMatch(t, []string{"e", "f"}, added)
|
require.ElementsMatch(t, []string{"e", "f"}, added)
|
||||||
require.ElementsMatch(t, []string{"a", "b"}, removed)
|
require.ElementsMatch(t, []string{"a", "a", "b"}, removed)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDiffEqualer(t *testing.T) {
|
||||||
|
a := []String{"c", "d", "e", "f"}
|
||||||
|
b := []String{"a", "a", "b", "c", "d"}
|
||||||
|
|
||||||
|
added, removed := DiffComparable(a, b)
|
||||||
|
|
||||||
|
require.ElementsMatch(t, []String{"e", "f"}, added)
|
||||||
|
require.ElementsMatch(t, []String{"a", "a", "b"}, removed)
|
||||||
}
|
}
|
||||||
|
28
slices/equal.go
Normal file
28
slices/equal.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package slices
|
||||||
|
|
||||||
|
// EqualComparableElements returns whether two slices have the same elements.
|
||||||
|
func EqualComparableElements[T comparable](a, b []T) bool {
|
||||||
|
extraA, extraB := DiffComparable(a, b)
|
||||||
|
|
||||||
|
if len(extraA) == 0 && len(extraB) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equaler defines a type that implements the Equal function.
|
||||||
|
type Equaler[T any] interface {
|
||||||
|
Equal(T) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// EqualEqualerElements returns whether two slices of Equaler have the same elements.
|
||||||
|
func EqualEqualerElements[T any, X Equaler[T]](a []T, b []X) bool {
|
||||||
|
extraA, extraB := DiffEqualer(a, b)
|
||||||
|
|
||||||
|
if len(extraA) == 0 && len(extraB) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
51
slices/equal_test.go
Normal file
51
slices/equal_test.go
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package slices
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEqualComparableElements(t *testing.T) {
|
||||||
|
a := []string{"a", "b", "c", "d"}
|
||||||
|
b := []string{"b", "c", "a", "d"}
|
||||||
|
|
||||||
|
ok := EqualComparableElements(a, b)
|
||||||
|
require.True(t, ok)
|
||||||
|
|
||||||
|
ok = EqualComparableElements(b, a)
|
||||||
|
require.True(t, ok)
|
||||||
|
|
||||||
|
a = append(a, "z")
|
||||||
|
|
||||||
|
ok = EqualComparableElements(a, b)
|
||||||
|
require.False(t, ok)
|
||||||
|
|
||||||
|
ok = EqualComparableElements(b, a)
|
||||||
|
require.False(t, ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
type String string
|
||||||
|
|
||||||
|
func (a String) Equal(b String) bool {
|
||||||
|
return string(a) == string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEqualEqualerElements(t *testing.T) {
|
||||||
|
a := []String{"a", "b", "c", "d"}
|
||||||
|
b := []String{"b", "c", "a", "d"}
|
||||||
|
|
||||||
|
ok := EqualEqualerElements(a, b)
|
||||||
|
require.True(t, ok)
|
||||||
|
|
||||||
|
ok = EqualEqualerElements(b, a)
|
||||||
|
require.True(t, ok)
|
||||||
|
|
||||||
|
a = append(a, "z")
|
||||||
|
|
||||||
|
ok = EqualEqualerElements(a, b)
|
||||||
|
require.False(t, ok)
|
||||||
|
|
||||||
|
ok = EqualEqualerElements(b, a)
|
||||||
|
require.False(t, ok)
|
||||||
|
}
|
Reference in New Issue
Block a user