Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Send cptv frame over dbus #128

Merged
merged 7 commits into from
Nov 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 19 additions & 18 deletions cmd/thermal-recorder/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,10 @@ const (
)

var (
version = "<not set>"
processor *motion.MotionProcessor
version = "<not set>"
processor *motion.MotionProcessor
headerInfo *headers.HeaderInfo = nil

frameLogIntervalFirstMin = 15
frameLogInterval = 60 * 5
)
Expand Down Expand Up @@ -105,8 +107,6 @@ func runMain() error {
return err
}

deleteSnapshot(conf.OutputDir)

log.Println("host initialisation")
if _, err := host.Init(); err != nil {
return err
Expand Down Expand Up @@ -144,33 +144,34 @@ func handleConn(conn net.Conn, conf *Config) error {
leptondController.SetAutoFFC(true)
totalFrames := 0
reader := bufio.NewReader(conn)
header, err := headers.ReadHeaderInfo(reader)
var err error
headerInfo, err = headers.ReadHeaderInfo(reader)
if err != nil {
return err
}

log.Printf("connection from %s %s (%dx%d@%dfps)", header.Brand(), header.Model(), header.ResX(), header.ResY(), header.FPS())
conf.LoadMotionConfig(header.Model())
log.Printf("connection from %s %s (%dx%d@%dfps)", headerInfo.Brand(), headerInfo.Model(), headerInfo.ResX(), headerInfo.ResY(), headerInfo.FPS())
conf.LoadMotionConfig(headerInfo.Model())
logConfig(conf)

parseFrame := frameParser(header.Brand(), header.Model())
parseFrame := frameParser(headerInfo.Brand(), headerInfo.Model())
if parseFrame == nil {
return fmt.Errorf("unable to handle frames for %s %s", header.Brand(), header.Model())
return fmt.Errorf("unable to handle frames for %s %s", headerInfo.Brand(), headerInfo.Model())
}

cptvRecorder := NewCPTVFileRecorder(conf, header, header.Brand(), header.Model(), header.CameraSerial(), header.Firmware())
cptvRecorder := NewCPTVFileRecorder(conf, headerInfo, headerInfo.Brand(), headerInfo.Model(), headerInfo.CameraSerial(), headerInfo.Firmware())
defer cptvRecorder.Stop()
var recorder recorder.Recorder = cptvRecorder

if conf.Throttler.Activate {
minRecordingLength := conf.Recorder.MinSecs + conf.Recorder.PreviewSecs
recorder = throttle.NewThrottledRecorder(cptvRecorder, &conf.Throttler, minRecordingLength, new(throttle.ThrottledEventRecorder), header)
recorder = throttle.NewThrottledRecorder(cptvRecorder, &conf.Throttler, minRecordingLength, new(throttle.ThrottledEventRecorder), headerInfo)
}

// Constant Recorder
var constantRecorder *CPTVFileRecorder
if conf.Recorder.ConstantRecorder {
constantRecorder = NewCPTVFileRecorder(conf, header, header.Brand(), header.Model(), header.CameraSerial(), header.Firmware())
constantRecorder = NewCPTVFileRecorder(conf, headerInfo, headerInfo.Brand(), headerInfo.Model(), headerInfo.CameraSerial(), headerInfo.Firmware())
constantRecorder.SetAsConstantRecorder()
}

Expand All @@ -181,14 +182,14 @@ func handleConn(conn net.Conn, conf *Config) error {
&conf.Location,
nil,
recorder,
header,
headerInfo,
constantRecorder,
)

log.Print("reading frames")
frameLogIntervalFirstMin *= header.FPS()
frameLogInterval *= header.FPS()
rawFrame := make([]byte, header.FrameSize())
frameLogIntervalFirstMin *= headerInfo.FPS()
frameLogInterval *= headerInfo.FPS()
rawFrame := make([]byte, headerInfo.FrameSize())
for {
_, err := io.ReadFull(reader, rawFrame[:5])
if err != nil {
Expand All @@ -197,7 +198,7 @@ func handleConn(conn net.Conn, conf *Config) error {
message := string(rawFrame[:5])
if message == clearBuffer {
log.Print("clearing motion buffer")
processor.Reset(header)
processor.Reset(headerInfo)
continue
}

Expand All @@ -209,7 +210,7 @@ func handleConn(conn net.Conn, conf *Config) error {
totalFrames++

if totalFrames%frameLogIntervalFirstMin == 0 &&
totalFrames <= 60*header.FPS() || totalFrames%frameLogInterval == 0 {
totalFrames <= 60*headerInfo.FPS() || totalFrames%frameLogInterval == 0 {
log.Printf("%d frames for this connection", totalFrames)
}

Expand Down
39 changes: 29 additions & 10 deletions cmd/thermal-recorder/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ package main
import (
"errors"

"github.com/TheCacophonyProject/go-cptv/cptvframe"
"github.com/TheCacophonyProject/thermal-recorder/headers"

"github.com/godbus/dbus"
"github.com/godbus/dbus/introspect"
)
Expand All @@ -29,7 +32,6 @@ const (
)

type service struct {
dir string
}

func startService(dir string) error {
Expand All @@ -45,12 +47,9 @@ func startService(dir string) error {
return errors.New("name already taken")
}

s := &service{
dir: dir,
}
s := &service{}
conn.Export(s, dbusPath, dbusName)
conn.Export(genIntrospectable(s), dbusPath, "org.freedesktop.DBus.Introspectable")

return nil
}

Expand All @@ -65,13 +64,33 @@ func genIntrospectable(v interface{}) introspect.Introspectable {
}

// TakeSnapshot will save the next frame as a still
func (s *service) TakeSnapshot() *dbus.Error {
err := newSnapshot(s.dir)
func (s *service) TakeSnapshot(lastFrame int) (*cptvframe.Frame, *dbus.Error) {
f, err := newSnapshot(lastFrame)
if err != nil {
return &dbus.Error{
Name: dbusName + ".StayOnForError",
return nil, &dbus.Error{
Name: dbusName + ".TakeSnapshot",
Body: []interface{}{err.Error()},
}
}
return nil
return f, nil
}
func (s *service) CameraInfo() (map[string]interface{}, *dbus.Error) {

if headerInfo == nil {
return nil, &dbus.Error{
Name: dbusName + ".NoHeaderInfo",
Body: nil,
}
}
camera_specs := map[string]interface{}{
headers.XResolution: headerInfo.ResX(),
headers.YResolution: headerInfo.ResY(),
headers.FrameSize: headerInfo.FrameSize(),
headers.Model: headerInfo.Model(),
headers.Brand: headerInfo.Brand(),
headers.FPS: headerInfo.FPS(),
headers.Serial: headerInfo.CameraSerial(),
headers.Firmware: headerInfo.Firmware(),
}
return camera_specs, nil
}
84 changes: 12 additions & 72 deletions cmd/thermal-recorder/snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,13 @@ package main

import (
"errors"
"image"
"image/color"
"image/png"
"log"
"math"
"os"
"path"
"sync"
"time"

"github.com/TheCacophonyProject/lepton3"
"github.com/TheCacophonyProject/go-cptv/cptvframe"
)

const (
snapshotName = "still.png"
allowedSnapshotPeriod = 500 * time.Millisecond
)

Expand All @@ -42,78 +34,26 @@ var (
mu sync.Mutex
)

func newSnapshot(dir string) error {
func newSnapshot(lastFrame int) (*cptvframe.Frame, error) {
mu.Lock()
defer mu.Unlock()

if time.Since(previousSnapshotTime) < allowedSnapshotPeriod {
return nil
return nil, nil
}

if processor == nil {
return errors.New("reading from camera has not started yet")
}
f := processor.GetRecentFrame()
if f == nil {
return errors.New("no frames yet")
}
g16 := image.NewGray16(image.Rect(0, 0, lepton3.FrameCols, lepton3.FrameRows))
// Max and min are needed for normalization of the frame
var valMax uint16
var valMin uint16 = math.MaxUint16
var id int
for _, row := range f.Pix {
for _, val := range row {
id += int(val)
valMax = maxUint16(valMax, val)
valMin = minUint16(valMin, val)
}
}

// Check if frame had already been processed
if id == previousSnapshotID {
return nil
return nil, errors.New("reading from camera has not started yet")
}
previousSnapshotID = id

var norm = math.MaxUint16 / (valMax - valMin)
for y, row := range f.Pix {
for x, val := range row {
g16.SetGray16(x, y, color.Gray16{Y: (val - valMin) * norm})
}
if lastFrame >= 0 && uint32(lastFrame) == processor.CurrentFrame {
return nil, errors.New("no new frames yet")
}

out, err := os.Create(path.Join(dir, snapshotName))
if err != nil {
return err
}
defer out.Close()

if err := png.Encode(out, g16); err != nil {
return err
}

// the time will be changed only if the attempt is successful
previousSnapshotTime = time.Now()
return nil
}

func deleteSnapshot(dir string) {
if err := os.Remove(path.Join(dir, snapshotName)); err != nil && !os.IsNotExist(err) {
log.Printf("error with deleting snapshot image %s", err)
}
}

func maxUint16(a, b uint16) uint16 {
if a > b {
return a
frameNum, f := processor.GetRecentFrame()
if f == nil {
return nil, errors.New("no frames yet")
}
return b
}

func minUint16(a, b uint16) uint16 {
if a < b {
return a
if f.Status.FrameCount == 0 {
f.Status.FrameCount = int(frameNum)
}
return b
return f, nil
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.15
require (
github.com/TheCacophonyProject/event-reporter v1.3.2-0.20200210010421-ca3fcb76a231
github.com/TheCacophonyProject/go-config v1.6.4
github.com/TheCacophonyProject/go-cptv v0.0.0-20201215230510-ae7134e91a71
github.com/TheCacophonyProject/go-cptv v0.0.0-20211109233846-8c32a5d161f7
github.com/TheCacophonyProject/lepton3 v0.0.0-20210324024142-003e5546e30f
github.com/TheCacophonyProject/window v0.0.0-20200312071457-7fc8799fdce7
github.com/alexflint/go-arg v1.3.0
Expand Down
11 changes: 8 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ github.com/TheCacophonyProject/go-config v1.6.4/go.mod h1:eDQfBjmTDh/l+2QLBgsotm
github.com/TheCacophonyProject/go-cptv v0.0.0-20200116020937-858bd8b71512/go.mod h1:8H6Aaft5549sIWxcsuCIL2o60/TQkoF93fVoSTpgZb8=
github.com/TheCacophonyProject/go-cptv v0.0.0-20200616224711-fc633122087a/go.mod h1:Vg73Ezn4kr8qDNP9LNgjki9qgi+5T/0Uc9oDyflaYUY=
github.com/TheCacophonyProject/go-cptv v0.0.0-20200818214604-bd5d4aa36043/go.mod h1:wG4/P/TsGtk33uBClYPjRlSbcdQrIASXutOUV8LMn2o=
github.com/TheCacophonyProject/go-cptv v0.0.0-20201215230510-ae7134e91a71 h1:g6XLYIt3hFo2JOQhbfySLxcFFv0kofg0L1Z7MyNXsLc=
github.com/TheCacophonyProject/go-cptv v0.0.0-20201215230510-ae7134e91a71/go.mod h1:pExPO/gk28kgWnd1z55xJ7YtC0KgQBDKvJoGYExc+l0=
github.com/TheCacophonyProject/go-cptv v0.0.0-20211109233846-8c32a5d161f7 h1:sf9KTj7u3mFMx5NsLpQtf8FtP3BDZAgtHutanDygQgk=
github.com/TheCacophonyProject/go-cptv v0.0.0-20211109233846-8c32a5d161f7/go.mod h1:T74NuMjo2YrLoyhAd0+9hj2pgVt8F7DWWzTMlU8aH6k=
github.com/TheCacophonyProject/lepton3 v0.0.0-20200121020734-2ae28662e1bc/go.mod h1:xzPAWtvVCbJdJC2Gn1cG0Ovs/VP7XGGiQpUU8wU4HME=
github.com/TheCacophonyProject/lepton3 v0.0.0-20200213011619-1934a9300bd3/go.mod h1:xzPAWtvVCbJdJC2Gn1cG0Ovs/VP7XGGiQpUU8wU4HME=
github.com/TheCacophonyProject/lepton3 v0.0.0-20200909032119-e2b2b778a8ee/go.mod h1:+FTQKx63hdEbuTe/nxNv9TQ2EWqdlzMZx7UNLGCX9SE=
Expand Down Expand Up @@ -97,6 +97,7 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
Expand All @@ -122,6 +123,8 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9
github.com/pelletier/go-toml v1.6.0 h1:aetoXYr0Tv7xRU/V4B4IZJ2QcbtMUFoNb3ORp7TzIK4=
github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
Expand All @@ -138,8 +141,9 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY=
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
Expand Down Expand Up @@ -172,6 +176,7 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
Expand Down
6 changes: 3 additions & 3 deletions headers/headerinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,7 @@ func ReadHeaderInfo(reader *bufio.Reader) (*HeaderInfo, error) {
if err != nil {
return nil, err
}

return &HeaderInfo{
header := &HeaderInfo{
resX: toInt(h[XResolution]),
resY: toInt(h[YResolution]),
fps: toInt(h[FPS]),
Expand All @@ -105,7 +104,8 @@ func ReadHeaderInfo(reader *bufio.Reader) (*HeaderInfo, error) {
model: toStr(h[Model]),
serial: toInt(h[Serial]),
firmware: toStr(h[Firmware]),
}, nil
}
return header, nil
}

func toInt(v interface{}) int {
Expand Down
7 changes: 5 additions & 2 deletions motion/motionprocessor.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ func NewMotionProcessor(
log: loglimiter.New(minLogInterval),
constantRecorder: constantRecorder,
constantRecording: !isNullOrNullPointer(constantRecorder),
CurrentFrame: 0,
}
}

Expand Down Expand Up @@ -89,6 +90,7 @@ type MotionProcessor struct {
constantRecording bool
constantRecorder recorder.Recorder
crFrames int
CurrentFrame uint32
}

type RecordingListener interface {
Expand All @@ -109,6 +111,7 @@ func (mp *MotionProcessor) Process(rawFrame []byte) error {
mp.stopConstantRecorder()
return err
}
mp.CurrentFrame += 1
mp.process(frame)
mp.processConstantRecorder(frame)
return nil
Expand Down Expand Up @@ -190,8 +193,8 @@ func (mp *MotionProcessor) ProcessFrame(srcFrame *cptvframe.Frame) {
mp.process(frame)
}

func (mp *MotionProcessor) GetRecentFrame() *cptvframe.Frame {
return mp.frameLoop.CopyRecent()
func (mp *MotionProcessor) GetRecentFrame() (uint32, *cptvframe.Frame) {
return mp.CurrentFrame, mp.frameLoop.CopyRecent()
}

func (mp *MotionProcessor) canStartWriting() error {
Expand Down