Skip to content

Commit

Permalink
Implement multiple google connections and autodetection
Browse files Browse the repository at this point in the history
Signed-off-by: Lukas Bachschwell <lukas@lbsfilm.at>
  • Loading branch information
s00500 committed May 15, 2021
1 parent 7d9cc71 commit 034a99e
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 55 deletions.
9 changes: 6 additions & 3 deletions Readme.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
# djifpvvideoout

This is a straightforward port of the dji fpv videooutscript (from [here](https://github.com/fpv-wtf/voc-poc)) to **golang**
This is a port of the dji fpv videooutscript (from [here](https://github.com/fpv-wtf/voc-poc)) to **golang**

The intersting thing is that it seems to **start working in low power mode** as well

Also this script should support unplugging and replugging USB, disconnecting / reconnecting the drone and should not care if it is started while the goggles are off

It will also work with **multiple googles at once**, so if you connect new ones while it is running it will open new ffplay instances for you

FFMpeg needs to be available in your path, it will be started by the go binary automatically

## Running

I usually run this like so:
go run .
Make sure you have go and ffmpeg installed, then run like this:

`go run .`

Crosscompilation does not work in an easy way due to the dependency on libusb

Expand Down
7 changes: 5 additions & 2 deletions ffmpeg.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ package main
import (
"io"
"os/exec"
"syscall"

log "github.com/s00500/env_logger"
)

// TODO: implement ways of ovveriding args
// TODO: Implement gst as well
func StartFFMPEGInstance() io.WriteCloser {
func StartFFMPEGInstance() (io.WriteCloser, func()) {
additionalArgs := []string{"-fast", "-flags2", "fast", "-fflags", "nobuffer", "-flags", "low_delay", "-strict", "experimental", "-vf", "setpts=N/60/TB", "-framedrop", "-sync", "ext", "-probesize", "32", "-analyzeduration", "0"}
args := []string{"-i", "-"}
args = append(args, additionalArgs...)
Expand All @@ -19,5 +20,7 @@ func StartFFMPEGInstance() io.WriteCloser {
log.Fatal("Could not get ffmpeg stdin")
}
log.MustFatal(cmd.Start())
return stdin
return stdin, func() {
cmd.Process.Signal(syscall.SIGKILL) // Not ellegant... could try sigterm and wait before...
}
}
142 changes: 92 additions & 50 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"fmt"
"io"
"sync"
"time"

log "github.com/s00500/env_logger"
Expand All @@ -13,73 +14,96 @@ import (
var magicStartBytes = []byte{0x52, 0x4d, 0x56, 0x54}

func main() {
log.Info("Starting")
log.Info("Starting djifpvvideout")

// TODO:
// Open video stream
// Write videostream to fifo, alternatively start omx via dbus and instantly output!
openPorts := make([]string, 0)
openPortsMu := sync.RWMutex{}

ctx := gousb.NewContext()
defer ctx.Close()

ffmpegIn := StartFFMPEGInstance()
vid, pid := gousb.ID(0x2ca3), gousb.ID(0x001f)

for {
// Open any device with a given VID/PID using a convenience function.
dev, err := ctx.OpenDeviceWithVIDPID(0x2ca3, 0x001f)
if err != nil {
log.Errorf("Could not open googles, retrying in 2 seconds: %v", err)
time.Sleep(time.Second * 2)
continue
}
if dev == nil {
log.Error("No device found, retrying in 2 seconds")
time.Sleep(time.Second * 2)
devs, err := ctx.OpenDevices(func(d *gousb.DeviceDesc) bool {
// this function is called for every device present.
// Returning true means the device should be opened.
if d.Vendor != vid || d.Product != pid {
return false
}

openPortsMu.RLock()
defer openPortsMu.RUnlock()
return !containsString(openPorts, fmt.Sprintf("%d.%d", d.Bus, d.Address))
})

log.MustFatal(err)
if len(devs) == 0 {
time.Sleep(time.Second * 3)
continue
}

// claim interface
intf, done, err := googleInterface(dev)
if err != nil {
log.Errorf("%s.GoogleInterface: %v", dev, err)
continue
}
log.Info("Found ", len(devs), " devices")

ep, err := intf.OutEndpoint(3)
if err != nil {
log.Errorf("%s.OutEndpoint: %v", intf, err)
continue
}
for _, devInArray := range devs {
dev := devInArray
openPortsMu.Lock()
openPorts = append(openPorts, fmt.Sprintf("%d.%d", dev.Desc.Bus, dev.Desc.Address))
openPortsMu.Unlock()

inEP, err := intf.InEndpoint(4)
if err != nil {
log.Errorf("%s.InEndpoint: %v", intf, err)
continue
}
go func() {
log.Infof("connecting to device on %d.%d", dev.Desc.Bus, dev.Desc.Address)
openStream(dev)
dev.Close()
openPortsMu.Lock()
openPorts = deleteElement(openPorts, fmt.Sprintf("%d.%d", dev.Desc.Bus, dev.Desc.Address))
openPortsMu.Unlock()
log.Warnf("lost device on %d.%d", dev.Desc.Bus, dev.Desc.Address)

// Write data to the USB device.
numBytes, err := ep.Write(magicStartBytes)
if numBytes != len(magicStartBytes) {
log.Errorf("%s.Write(%d): only %d bytes written, returned error is %v", ep, len(magicStartBytes), numBytes, err)
continue
}()
}
//log.Println("bytes successfully sent to the endpoint")
time.Sleep(time.Second * 3)
}
}

stream, err := inEP.NewStream(512, 3) // Took default form github
if err != nil {
log.Errorf("Could not open stream: %v", intf, err)
continue
}
func openStream(dev *gousb.Device) {
ffmpegIn, stopPlayer := StartFFMPEGInstance()
// claim interface
intf, done, err := googleInterface(dev)
if err != nil {
log.Errorf("%s.GoogleInterface: %v", dev, err)
return
}

num, err := io.Copy(ffmpegIn, stream)
//num, err := io.Copy(os.Stdout, stream)
//_, err = io.Copy(ioutil.Discard, stream)
if !log.Should(err) {
log.Info("Wrote ", num, " bytes")
}
done()
dev.Close()
ep, err := intf.OutEndpoint(3)
if err != nil {
log.Errorf("%s.OutEndpoint: %v", intf, err)
return
}

inEP, err := intf.InEndpoint(4)
if err != nil {
log.Errorf("%s.InEndpoint: %v", intf, err)
return
}

// Write data to the USB device.
numBytes, err := ep.Write(magicStartBytes)
if numBytes != len(magicStartBytes) {
log.Errorf("%s.Write(%d): only %d bytes written, returned error is %v", ep, len(magicStartBytes), numBytes, err)
return
}
//log.Println("bytes successfully sent to the endpoint")

stream, err := inEP.NewStream(512, 3) // Took default form github
if err != nil {
log.Errorf("Could not open stream: %v", intf, err)
return
}

io.Copy(ffmpegIn, stream)
stopPlayer()
done()
}

func googleInterface(d *gousb.Device) (intf *gousb.Interface, done func(), err error) {
Expand All @@ -101,3 +125,21 @@ func googleInterface(d *gousb.Device) (intf *gousb.Interface, done func(), err e
cfg.Close()
}, nil
}

func containsString(all []string, one string) bool {
for _, s := range all {
if s == one {
return true
}
}
return false
}

func deleteElement(all []string, one string) []string {
for index, elem := range all {
if elem == one {
return append(all[:index], all[index+1:]...)
}
}
return all
}

0 comments on commit 034a99e

Please sign in to comment.