-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcamera.go
140 lines (121 loc) · 3 KB
/
camera.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package esphome
import (
"bytes"
"image"
"image/jpeg"
"time"
"github.com/golang/protobuf/proto"
"maze.io/x/esphome/api"
)
// Camera is an ESP32 camera.
type Camera struct {
Entity
lastFrame time.Time
}
func newCamera(client *Client, entity *api.ListEntitiesCameraResponse) *Camera {
return &Camera{
Entity: Entity{
Name: entity.Name,
ObjectID: entity.ObjectId,
UniqueID: entity.UniqueId,
Key: entity.Key,
client: client,
},
}
}
// Image grabs one image frame from the camera.
func (entity *Camera) Image() (image.Image, error) {
if err := entity.client.sendTimeout(&api.CameraImageRequest{
Stream: true,
}, entity.client.Timeout); err != nil {
return nil, err
}
var (
in = make(chan proto.Message, 1)
out = make(chan []byte)
)
entity.client.waitMutex.Lock()
entity.client.wait[api.CameraImageResponseType] = in
entity.client.waitMutex.Unlock()
go func(in <-chan proto.Message, out chan []byte) {
for message := range in {
if message, ok := message.(*api.CameraImageResponse); ok {
out <- message.Data
if message.Done {
close(out)
entity.lastFrame = time.Now()
return
}
}
}
}(in, out)
var buffer = new(bytes.Buffer)
for chunk := range out {
buffer.Write(chunk)
}
entity.client.waitMutex.Lock()
delete(entity.client.wait, api.CameraImageResponseType)
entity.client.waitMutex.Unlock()
return jpeg.Decode(buffer)
}
// Stream returns a channel with raw image frame buffers.
func (entity *Camera) Stream() (<-chan *bytes.Buffer, error) {
if err := entity.client.sendTimeout(&api.CameraImageRequest{
Stream: true,
}, entity.client.Timeout); err != nil {
return nil, err
}
in := make(chan proto.Message, 1)
entity.client.waitMutex.Lock()
entity.client.wait[api.CameraImageResponseType] = in
entity.client.waitMutex.Unlock()
out := make(chan *bytes.Buffer)
go func(in <-chan proto.Message, out chan<- *bytes.Buffer) {
var (
ticker = time.NewTicker(time.Second)
buffer = new(bytes.Buffer)
)
defer ticker.Stop()
for {
select {
case message := <-in:
frame := message.(*api.CameraImageResponse)
buffer.Write(frame.Data)
if frame.Done {
out <- buffer
buffer = new(bytes.Buffer)
entity.lastFrame = time.Now()
}
case <-ticker.C:
if err := entity.client.sendTimeout(&api.CameraImageRequest{
Stream: true,
}, entity.client.Timeout); err != nil {
close(out)
return
}
}
}
}(in, out)
return out, nil
}
// ImageStream is like Stream, returning decoded frame images.
func (entity *Camera) ImageStream() (<-chan image.Image, error) {
in, err := entity.Stream()
if err != nil {
return nil, err
}
out := make(chan image.Image)
go func(in <-chan *bytes.Buffer, out chan image.Image) {
defer close(out)
for frame := range in {
if i, err := jpeg.Decode(frame); err == nil {
out <- i
}
}
}(in, out)
return out, nil
}
// LastFrame returns the time of the last camera frame received.
func (entity *Camera) LastFrame() time.Time {
return entity.lastFrame
}