Move cmd module to internal
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
package device
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// https://trac.ffmpeg.org/wiki/Capture/Webcam
|
||||
const deviceInputPrefix = "-f avfoundation"
|
||||
|
||||
func deviceInputSuffix(videoIdx, audioIdx int) string {
|
||||
video := findMedia(core.KindVideo, videoIdx)
|
||||
audio := findMedia(core.KindAudio, audioIdx)
|
||||
switch {
|
||||
case video != nil && audio != nil:
|
||||
return `"` + video.ID + `:` + audio.ID + `"`
|
||||
case video != nil:
|
||||
return `"` + video.ID + `"`
|
||||
case audio != nil:
|
||||
return `"` + audio.ID + `"`
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func loadMedias() {
|
||||
cmd := exec.Command(
|
||||
Bin, "-hide_banner", "-list_devices", "true", "-f", "avfoundation", "-i", "dummy",
|
||||
)
|
||||
|
||||
var buf bytes.Buffer
|
||||
cmd.Stderr = &buf
|
||||
_ = cmd.Run()
|
||||
|
||||
var kind string
|
||||
|
||||
lines := strings.Split(buf.String(), "\n")
|
||||
process:
|
||||
for _, line := range lines {
|
||||
switch {
|
||||
case strings.HasSuffix(line, "video devices:"):
|
||||
kind = core.KindVideo
|
||||
continue
|
||||
case strings.HasSuffix(line, "audio devices:"):
|
||||
kind = core.KindAudio
|
||||
continue
|
||||
case strings.HasPrefix(line, "dummy"):
|
||||
break process
|
||||
}
|
||||
|
||||
// [AVFoundation indev @ 0x7fad54604380] [0] FaceTime HD Camera
|
||||
name := line[42:]
|
||||
media := loadMedia(kind, name)
|
||||
medias = append(medias, media)
|
||||
}
|
||||
}
|
||||
|
||||
func loadMedia(kind, name string) *core.Media {
|
||||
return &core.Media{Kind: kind, ID: name}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package device
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||
"io/ioutil"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// https://trac.ffmpeg.org/wiki/Capture/Webcam
|
||||
const deviceInputPrefix = "-f v4l2"
|
||||
|
||||
func deviceInputSuffix(videoIdx, audioIdx int) string {
|
||||
if video := findMedia(core.KindVideo, videoIdx); video != nil {
|
||||
return video.ID
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func loadMedias() {
|
||||
files, err := ioutil.ReadDir("/dev")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, file := range files {
|
||||
log.Trace().Msg("[ffmpeg] " + file.Name())
|
||||
if strings.HasPrefix(file.Name(), core.KindVideo) {
|
||||
media := loadMedia(core.KindVideo, "/dev/"+file.Name())
|
||||
if media != nil {
|
||||
medias = append(medias, media)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func loadMedia(kind, name string) *core.Media {
|
||||
cmd := exec.Command(
|
||||
Bin, "-hide_banner", "-f", "v4l2", "-list_formats", "all", "-i", name,
|
||||
)
|
||||
var buf bytes.Buffer
|
||||
cmd.Stderr = &buf
|
||||
_ = cmd.Run()
|
||||
|
||||
if !bytes.Contains(buf.Bytes(), []byte("Raw")) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &core.Media{Kind: kind, ID: name}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package device
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// https://trac.ffmpeg.org/wiki/DirectShow
|
||||
const deviceInputPrefix = "-f dshow"
|
||||
|
||||
func deviceInputSuffix(videoIdx, audioIdx int) string {
|
||||
video := findMedia(core.KindVideo, videoIdx)
|
||||
audio := findMedia(core.KindAudio, audioIdx)
|
||||
switch {
|
||||
case video != nil && audio != nil:
|
||||
return `video="` + video.ID + `":audio=` + audio.ID + `"`
|
||||
case video != nil:
|
||||
return `video="` + video.ID + `"`
|
||||
case audio != nil:
|
||||
return `audio="` + audio.ID + `"`
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func loadMedias() {
|
||||
cmd := exec.Command(
|
||||
Bin, "-hide_banner", "-list_devices", "true", "-f", "dshow", "-i", "",
|
||||
)
|
||||
|
||||
var buf bytes.Buffer
|
||||
cmd.Stderr = &buf
|
||||
_ = cmd.Run()
|
||||
|
||||
lines := strings.Split(buf.String(), "\r\n")
|
||||
for _, line := range lines {
|
||||
var kind string
|
||||
if strings.HasSuffix(line, "(video)") {
|
||||
kind = core.KindVideo
|
||||
} else if strings.HasSuffix(line, "(audio)") {
|
||||
kind = core.KindAudio
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
|
||||
// hope we have constant prefix and suffix sizes
|
||||
// [dshow @ 00000181e8d028c0] "VMware Virtual USB Video Device" (video)
|
||||
name := line[28 : len(line)-9]
|
||||
media := loadMedia(kind, name)
|
||||
medias = append(medias, media)
|
||||
}
|
||||
}
|
||||
|
||||
func loadMedia(kind, name string) *core.Media {
|
||||
return &core.Media{Kind: kind, ID: name}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package device
|
||||
|
||||
import (
|
||||
"github.com/AlexxIT/go2rtc/internal/api"
|
||||
"github.com/AlexxIT/go2rtc/internal/app"
|
||||
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||
"github.com/rs/zerolog"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func Init() {
|
||||
log = app.GetLogger("exec")
|
||||
|
||||
api.HandleFunc("api/devices", handle)
|
||||
}
|
||||
|
||||
func GetInput(src string) (string, error) {
|
||||
if medias == nil {
|
||||
loadMedias()
|
||||
}
|
||||
|
||||
input := deviceInputPrefix
|
||||
|
||||
var videoIdx, audioIdx int
|
||||
if i := strings.IndexByte(src, '?'); i > 0 {
|
||||
query, err := url.ParseQuery(src[i+1:])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for key, value := range query {
|
||||
switch key {
|
||||
case "video":
|
||||
videoIdx, _ = strconv.Atoi(value[0])
|
||||
case "audio":
|
||||
audioIdx, _ = strconv.Atoi(value[0])
|
||||
case "framerate":
|
||||
input += " -framerate " + value[0]
|
||||
case "resolution":
|
||||
input += " -video_size " + value[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
input += " -i " + deviceInputSuffix(videoIdx, audioIdx)
|
||||
|
||||
return input, nil
|
||||
}
|
||||
|
||||
var Bin string
|
||||
var log zerolog.Logger
|
||||
var medias []*core.Media
|
||||
|
||||
func findMedia(kind string, index int) *core.Media {
|
||||
for _, media := range medias {
|
||||
if media.Kind != kind {
|
||||
continue
|
||||
}
|
||||
if index == 0 {
|
||||
return media
|
||||
}
|
||||
index--
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func handle(w http.ResponseWriter, r *http.Request) {
|
||||
if medias == nil {
|
||||
loadMedias()
|
||||
}
|
||||
|
||||
var items []api.Stream
|
||||
var iv, ia int
|
||||
|
||||
for _, media := range medias {
|
||||
var source string
|
||||
switch media.Kind {
|
||||
case core.KindVideo:
|
||||
source = "ffmpeg:device?video=" + strconv.Itoa(iv)
|
||||
iv++
|
||||
case core.KindAudio:
|
||||
source = "ffmpeg:device?audio=" + strconv.Itoa(ia)
|
||||
ia++
|
||||
}
|
||||
items = append(items, api.Stream{Name: media.ID, URL: source})
|
||||
}
|
||||
|
||||
api.ResponseStreams(w, items)
|
||||
}
|
||||
Reference in New Issue
Block a user