Skip to content

Commit 4231989

Browse files
committed
add windows DirectShow support
1 parent efcd524 commit 4231989

File tree

8 files changed

+147
-9
lines changed

8 files changed

+147
-9
lines changed

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2021 noriah vix ([email protected])
3+
Copyright (c) 2022 noriah vix ([email protected])
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@
1717
## it supports audio backends
1818
- PortAudio (linux/macOS/*windblows**)
1919
- PulseAudio (parec/FFmpeg)
20-
- AVFoundation (FFmpeg)
21-
- ALSA (FFmpeg)
20+
- AVFoundation (macOS FFmpeg)
21+
- DirectShow (windblows FFmpeg)
22+
- ALSA (linux/FFmpeg)
2223

2324
## it depends on
2425

fft/fftw.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
//go:build cgo
2-
// +build cgo
1+
//go:build cgo && !windows
32

43
package fft
54

fft/gonum.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
//go:build !cgo
2-
// +build !cgo
1+
//go:build !cgo || windows
32

43
package fft
54

input/ffmpeg/alsa.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
//go:build linux
2+
13
package ffmpeg
24

35
import (

input/ffmpeg/avfoundation.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
//go:build darwin
2+
13
package ffmpeg
24

35
import (

input/ffmpeg/dshow.go

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
//go:build windows
2+
3+
package ffmpeg
4+
5+
import (
6+
"bufio"
7+
"bytes"
8+
"fmt"
9+
"os/exec"
10+
"strings"
11+
12+
"github.com/noriah/catnip/input"
13+
"github.com/noriah/catnip/input/common/execread"
14+
)
15+
16+
func init() {
17+
input.RegisterBackend("ffmpeg-dshow", DShow{})
18+
}
19+
20+
func NewWindowsSession(b FFmpegBackend, cfg input.SessionConfig) (*execread.Session, error) {
21+
args := []string{"ffmpeg", "-hide_banner", "-loglevel", "panic"}
22+
args = append(args,
23+
"-f", "dshow", "-audio_buffer_size", "20",
24+
"-sample_rate", fmt.Sprintf("%.0f", cfg.SampleRate),
25+
"-channels", fmt.Sprintf("%d", cfg.FrameSize),
26+
// "-sample_size", fmt.Sprintf("%d", 16),
27+
)
28+
args = append(args, b.InputArgs()...)
29+
args = append(args, "-f", "f64le", "-")
30+
31+
return execread.NewSession(args, false, cfg)
32+
}
33+
34+
// DShow is the DirectShow input for FFmpeg on Windows.
35+
type DShow struct{}
36+
37+
func (p DShow) Init() error {
38+
return nil
39+
}
40+
41+
func (p DShow) Close() error {
42+
return nil
43+
}
44+
45+
// Devices returns a list of dshow devices.
46+
func (p DShow) Devices() ([]input.Device, error) {
47+
cmd := exec.Command(
48+
"ffmpeg", "-hide_banner", "-loglevel", "info",
49+
"-f", "dshow", "-list_devices", "true",
50+
"-i", "",
51+
)
52+
53+
o, _ := cmd.CombinedOutput()
54+
55+
audio := true
56+
var devices []input.Device
57+
58+
var scanner = bufio.NewScanner(bytes.NewReader(o))
59+
60+
for scanner.Scan() {
61+
text := scanner.Text()
62+
63+
// Trim away the prefix.
64+
if strings.HasPrefix(text, "[dshow") {
65+
parts := strings.SplitN(text, "] ", 2)
66+
if len(parts) == 2 {
67+
text = parts[1][1:]
68+
}
69+
}
70+
71+
// If we're not scanning a device (which starts with a square bracket)
72+
// anymore, then we stop.
73+
if strings.HasPrefix(text, ":") {
74+
audio = false
75+
continue
76+
}
77+
78+
// If we're not under the audio section yet, then skip.
79+
if !audio {
80+
continue
81+
}
82+
83+
// Parse.
84+
parts := strings.SplitN(text, "\" (", 2)
85+
if len(parts) != 2 {
86+
continue
87+
}
88+
89+
if !strings.HasPrefix(parts[1], "audio") {
90+
continue
91+
}
92+
93+
devices = append(devices, DShowDevice{
94+
Name: parts[0],
95+
})
96+
}
97+
98+
if len(devices) == 0 {
99+
// This is completely for visual.
100+
var lines = strings.Split(string(o), "\n")
101+
for i, line := range lines {
102+
lines[i] = "\t" + line
103+
}
104+
var output = strings.Join(lines, "\n")
105+
106+
return nil, fmt.Errorf("no devices found; ffmpeg output:\n%s", output)
107+
}
108+
109+
return devices, nil
110+
}
111+
112+
func (p DShow) DefaultDevice() (input.Device, error) {
113+
return DShowDevice{"no default device"}, nil
114+
}
115+
116+
func (p DShow) Start(cfg input.SessionConfig) (input.Session, error) {
117+
dv, ok := cfg.Device.(DShowDevice)
118+
if !ok {
119+
return nil, fmt.Errorf("invalid device type %T", cfg.Device)
120+
}
121+
122+
return NewWindowsSession(dv, cfg)
123+
}
124+
125+
type DShowDevice struct {
126+
Name string
127+
}
128+
129+
func (d DShowDevice) InputArgs() []string {
130+
input := fmt.Sprintf("audio=%s", d.Name)
131+
return []string{"-i", input}
132+
}
133+
134+
func (d DShowDevice) String() string {
135+
return fmt.Sprintf("%s", d.Name)
136+
}

portaudio.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
//go:build cgo && !noportaudio
2-
// +build cgo,!noportaudio
1+
//go:build (cgo && !windows && !noportaudio) || (cgo && windows && enablePortaudio)
32

43
package main
54

0 commit comments

Comments
 (0)