//go:build linux && (386 || arm || mipsle || amd64 || arm64) package v4l2 import ( "encoding/binary" "fmt" "net/http" "os" "strings" "github.com/AlexxIT/go2rtc/internal/api" "github.com/AlexxIT/go2rtc/internal/streams" "github.com/AlexxIT/go2rtc/pkg/core" "github.com/AlexxIT/go2rtc/pkg/v4l2" "github.com/AlexxIT/go2rtc/pkg/v4l2/device" ) func Init() { streams.HandleFunc("v4l2", func(source string) (core.Producer, error) { return v4l2.Open(source) }) api.HandleFunc("api/v4l2", apiV4L2) } func apiV4L2(w http.ResponseWriter, r *http.Request) { files, err := os.ReadDir("/dev") if err != nil { return } var sources []*api.Source for _, file := range files { if !strings.HasPrefix(file.Name(), core.KindVideo) { continue } path := "/dev/" + file.Name() dev, err := device.Open(path) if err != nil { continue } formats, _ := dev.ListFormats() for _, fourCC := range formats { name, ffmpeg := findFormat(fourCC) source := &api.Source{Name: name} sizes, _ := dev.ListSizes(fourCC) for _, wh := range sizes { if source.Info != "" { source.Info += " " } source.Info += fmt.Sprintf("%dx%d", wh[0], wh[1]) frameRates, _ := dev.ListFrameRates(fourCC, wh[0], wh[1]) for _, fr := range frameRates { source.Info += fmt.Sprintf("@%d", fr) if source.URL == "" && ffmpeg != "" { source.URL = fmt.Sprintf( "v4l2:device?video=%s&input_format=%s&video_size=%dx%d&framerate=%d", path, ffmpeg, wh[0], wh[1], fr, ) } } } if source.Info != "" { sources = append(sources, source) } } _ = dev.Close() } api.ResponseSources(w, sources) } func findFormat(fourCC uint32) (name, ffmpeg string) { for _, format := range device.Formats { if format.FourCC == fourCC { return format.Name, format.FFmpeg } } return string(binary.LittleEndian.AppendUint32(nil, fourCC)), "" }