1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "extensions/browser/api/webcam_private/visca_webcam.h"
8 #include "base/message_loop/message_loop.h"
9 #include "content/public/browser/browser_thread.h"
11 using content::BrowserThread
;
15 // Message terminator:
16 const char VISCA_TERMINATOR
= 0xFF;
19 const char VISCA_RESPONSE_NETWORK_CHANGE
= 0x38;
20 const char VISCA_RESPONSE_ACK
= 0x40;
21 const char VISCA_RESPONSE_ERROR
= 0x60;
23 // The default pan speed is MAX_PAN_SPEED /2 and the default tilt speed is
24 // MAX_TILT_SPEED / 2.
25 const int MAX_PAN_SPEED
= 0x18;
26 const int MAX_TILT_SPEED
= 0x14;
28 // Pan-Tilt-Zoom movement comands from http://www.manualslib.com/manual/...
29 // 557364/Cisco-Precisionhd-1080p12x.html?page=31#manual
31 // Reset the address of each device in the VISCA chain (broadcast). This is used
32 // when resetting the VISCA network.
33 const std::vector
<char> kSetAddressCommand
= {0x88, 0x30, 0x01, 0xFF};
35 // Clear all of the devices, halting any pending commands in the VISCA chain
36 // (broadcast). This is used when resetting the VISCA network.
37 const std::vector
<char> kClearAllCommand
= {0x88, 0x01, 0x00, 0x01, 0xFF};
39 // Clear the command buffer in the target device and cancel the command
40 // currently being executed. Command: {0x8X, 0x01, 0x00, 0x01, 0xFF}, X = 1 to
41 // 7: target device address.
42 const std::vector
<char> kClearCommand
= {0x81, 0x01, 0x00, 0x01, 0xFF};
44 // Command: {0x8X, 0x09, 0x06, 0x12, 0xFF}, X = 1 to 7: target device address.
45 // Response: {0xY0, 0x50, 0x0p, 0x0q, 0x0r, 0x0s, 0x0t, 0x0u, 0x0v, 0x0w, 0xFF},
46 // Y = socket number; pqrs: pan position; tuvw: tilt position.
47 const std::vector
<char> kGetPanTiltCommand
= {0x81, 0x09, 0x06, 0x12, 0xFF};
49 // Command: {0x8X, 0x01, 0x06, 0x02, 0x0p, 0x0t, 0x0q, 0x0r, 0x0s, 0x0u, 0x0v,
50 // 0x0w, 0x0y, 0x0z, 0xFF}, X = 1 to 7: target device address; p = pan speed;
51 // t = tilt speed; qrsu = pan position; vwyz = tilt position.
52 const std::vector
<char> kSetPanTiltCommand
= {0x81, 0x01, 0x06, 0x02, 0x00,
53 0x00, 0x00, 0x00, 0x00, 0x00,
54 0x00, 0x00, 0x00, 0x00, 0xFF};
56 // Command: {0x8X, 0x01, 0x06, 0x05, 0xFF}, X = 1 to 7: target device address.
57 const std::vector
<char> kResetPanTiltCommand
= {0x81, 0x01, 0x06, 0x05, 0xFF};
59 // Command: {0x8X, 0x09, 0x04, 0x47, 0xFF}, X = 1 to 7: target device address.
60 // Response: {0xY0, 0x50, 0x0p, 0x0q, 0x0r, 0x0s, 0xFF}, Y = socket number;
61 // pqrs: zoom position.
62 const std::vector
<char> kGetZoomCommand
= {0x81, 0x09, 0x04, 0x47, 0xFF};
64 // Command: {0x8X, 0x01, 0x04, 0x47, 0x0p, 0x0q, 0x0r, 0x0s, 0xFF}, X = 1 to 7:
65 // target device address; pqrs: zoom position;
66 const std::vector
<char> kSetZoomCommand
=
67 {0x81, 0x01, 0x04, 0x47, 0x00, 0x00, 0x00, 0x00, 0xFF};
69 // Command: {0x8X, 0x01, 0x06, 0x01, 0x0p, 0x0t, 0x03, 0x01, 0xFF}, X = 1 to 7:
70 // target device address; p: pan speed; t: tilt speed.
71 const std::vector
<char> kPTUpCommand
= {0x81, 0x01, 0x06, 0x01, 0x00,
72 0x00, 0x03, 0x01, 0xFF};
74 // Command: {0x8X, 0x01, 0x06, 0x01, 0x0p, 0x0t, 0x03, 0x02, 0xFF}, X = 1 to 7:
75 // target device address; p: pan speed; t: tilt speed.
76 const std::vector
<char> kPTDownCommand
= {0x81, 0x01, 0x06, 0x01, 0x00,
77 0x00, 0x03, 0x02, 0xFF};
79 // Command: {0x8X, 0x01, 0x06, 0x01, 0x0p, 0x0t, 0x0, 0x03, 0xFF}, X = 1 to 7:
80 // target device address; p: pan speed; t: tilt speed.
81 const std::vector
<char> kPTLeftCommand
= {0x81, 0x01, 0x06, 0x01, 0x00,
82 0x00, 0x01, 0x03, 0xFF};
84 // Command: {0x8X, 0x01, 0x06, 0x01, 0x0p, 0x0t, 0x02, 0x03, 0xFF}, X = 1 to 7:
85 // target device address; p: pan speed; t: tilt speed.
86 const std::vector
<char> kPTRightCommand
= {0x81, 0x01, 0x06, 0x01, 0x00,
87 0x00, 0x02, 0x03, 0xFF};
89 // Command: {0x8X, 0x01, 0x06, 0x01, 0x03, 0x03, 0x03, 0x03, 0xFF}, X = 1 to 7:
90 // target device address.
91 const std::vector
<char> kPTStopCommand
=
92 {0x81, 0x01, 0x06, 0x01, 0x03, 0x03, 0x03, 0x03, 0xFF};
96 namespace extensions
{
98 ViscaWebcam::ViscaWebcam(const std::string
& path
,
99 const std::string
& extension_id
)
101 extension_id_(extension_id
),
104 weak_ptr_factory_(this) {}
106 ViscaWebcam::~ViscaWebcam() {
109 void ViscaWebcam::Open(const OpenCompleteCallback
& open_callback
) {
110 BrowserThread::PostTask(
111 BrowserThread::IO
, FROM_HERE
,
112 base::Bind(&ViscaWebcam::OpenOnIOThread
, weak_ptr_factory_
.GetWeakPtr(),
116 void ViscaWebcam::OpenOnIOThread(const OpenCompleteCallback
& open_callback
) {
117 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
119 api::serial::ConnectionOptions options
;
121 // Set the receive buffer size to receive the response data 1 by 1.
122 options
.buffer_size
.reset(new int(1));
123 options
.persistent
.reset(new bool(false));
124 options
.bitrate
.reset(new int(9600));
125 options
.cts_flow_control
.reset(new bool(false));
126 options
.receive_timeout
.reset(new int(0));
127 options
.send_timeout
.reset(new int(0));
128 options
.data_bits
= api::serial::DATA_BITS_EIGHT
;
129 options
.parity_bit
= api::serial::PARITY_BIT_NO
;
130 options
.stop_bits
= api::serial::STOP_BITS_ONE
;
132 serial_connection_
.reset(new SerialConnection(path_
, extension_id_
));
133 serial_connection_
->Open(
134 options
, base::Bind(&ViscaWebcam::OnConnected
,
135 weak_ptr_factory_
.GetWeakPtr(), open_callback
));
138 void ViscaWebcam::OnConnected(const OpenCompleteCallback
& open_callback
,
141 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
142 base::Bind(open_callback
, false));
144 Send(kSetAddressCommand
,
145 base::Bind(&ViscaWebcam::OnAddressSetCompleted
,
146 weak_ptr_factory_
.GetWeakPtr(), open_callback
));
150 void ViscaWebcam::OnAddressSetCompleted(
151 const OpenCompleteCallback
& open_callback
,
153 const std::vector
<char>& response
) {
154 commands_
.pop_front();
156 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
157 base::Bind(open_callback
, false));
159 Send(kClearAllCommand
,
160 base::Bind(&ViscaWebcam::OnClearAllCompleted
,
161 weak_ptr_factory_
.GetWeakPtr(), open_callback
));
165 void ViscaWebcam::OnClearAllCompleted(const OpenCompleteCallback
& open_callback
,
167 const std::vector
<char>& response
) {
168 commands_
.pop_front();
170 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
171 base::Bind(open_callback
, false));
173 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
174 base::Bind(open_callback
, true));
178 void ViscaWebcam::Send(const std::vector
<char>& command
,
179 const CommandCompleteCallback
& callback
) {
180 commands_
.push_back(std::make_pair(command
, callback
));
181 // If this is the only command in the queue, send it now.
182 if (commands_
.size() == 1) {
183 serial_connection_
->Send(
184 command
, base::Bind(&ViscaWebcam::OnSendCompleted
,
185 weak_ptr_factory_
.GetWeakPtr(), callback
));
189 void ViscaWebcam::OnSendCompleted(const CommandCompleteCallback
& callback
,
191 api::serial::SendError error
) {
192 if (error
== api::serial::SEND_ERROR_NONE
) {
193 ReceiveLoop(callback
);
195 const std::vector
<char> response
;
196 callback
.Run(false, response
);
200 void ViscaWebcam::ReceiveLoop(const CommandCompleteCallback
& callback
) {
201 serial_connection_
->Receive(base::Bind(&ViscaWebcam::OnReceiveCompleted
,
202 weak_ptr_factory_
.GetWeakPtr(),
206 void ViscaWebcam::OnReceiveCompleted(const CommandCompleteCallback
& callback
,
207 const std::vector
<char>& data
,
208 api::serial::ReceiveError error
) {
209 data_buffer_
.insert(data_buffer_
.end(), data
.begin(), data
.end());
211 if (error
== api::serial::RECEIVE_ERROR_NONE
) {
212 // Loop until encounter the terminator.
213 if (int(data_buffer_
.back()) != VISCA_TERMINATOR
) {
214 base::MessageLoop::current()->PostTask(
215 FROM_HERE
, base::Bind(&ViscaWebcam::ReceiveLoop
,
216 weak_ptr_factory_
.GetWeakPtr(), callback
));
218 // Clear |data_buffer_|.
219 std::vector
<char> response
;
220 response
.swap(data_buffer_
);
222 if ((int(response
[1]) & 0xF0) == VISCA_RESPONSE_ERROR
) {
223 callback
.Run(false, response
);
224 } else if ((int(response
[1]) & 0xF0) != VISCA_RESPONSE_ACK
&&
225 (int(response
[1]) & 0xFF) != VISCA_RESPONSE_NETWORK_CHANGE
) {
226 callback
.Run(true, response
);
228 base::MessageLoop::current()->PostTask(
229 FROM_HERE
, base::Bind(&ViscaWebcam::ReceiveLoop
,
230 weak_ptr_factory_
.GetWeakPtr(), callback
));
234 // Clear |data_buffer_|.
235 std::vector
<char> response
;
236 response
.swap(data_buffer_
);
237 callback
.Run(false, response
);
241 void ViscaWebcam::OnCommandCompleted(const SetPTZCompleteCallback
& callback
,
243 const std::vector
<char>& response
) {
244 // TODO(xdai): Error handling according to |response|.
245 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
246 base::Bind(callback
, success
));
247 commands_
.pop_front();
249 // If there are pending commands, process the next one.
250 if (!commands_
.empty()) {
251 const std::vector
<char> next_command
= commands_
.front().first
;
252 const CommandCompleteCallback next_callback
= commands_
.front().second
;
253 serial_connection_
->Send(
255 base::Bind(&ViscaWebcam::OnSendCompleted
,
256 weak_ptr_factory_
.GetWeakPtr(), next_callback
));
260 void ViscaWebcam::OnInquiryCompleted(InquiryType type
,
261 const GetPTZCompleteCallback
& callback
,
263 const std::vector
<char>& response
) {
266 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
267 base::Bind(callback
, false, value
));
271 // See kGetPanTiltCommand for the format of response.
272 pan_
= ((int(response
[2]) & 0x0F) << 12) +
273 ((int(response
[3]) & 0x0F) << 8) +
274 ((int(response
[4]) & 0x0F) << 4) + (int(response
[5]) & 0x0F);
278 // See kGetPanTiltCommand for the format of response.
279 tilt_
= ((int(response
[6]) & 0x0F) << 12) +
280 ((int(response
[7]) & 0x0F) << 8) +
281 ((int(response
[8]) & 0x0F) << 4) + (int(response
[9]) & 0x0F);
285 // See kGetZoomCommand for the format of response.
286 value
= ((int(response
[2]) & 0x0F) << 12) +
287 ((int(response
[3]) & 0x0F) << 8) +
288 ((int(response
[4]) & 0x0F) << 4) + (int(response
[5]) & 0x0F);
291 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
292 base::Bind(callback
, true, value
));
294 commands_
.pop_front();
296 // If there are pending commands, process the next one.
297 if (!commands_
.empty()) {
298 const std::vector
<char> next_command
= commands_
.front().first
;
299 const CommandCompleteCallback next_callback
= commands_
.front().second
;
300 serial_connection_
->Send(
302 base::Bind(&ViscaWebcam::OnSendCompleted
,
303 weak_ptr_factory_
.GetWeakPtr(), next_callback
));
307 void ViscaWebcam::GetPan(const GetPTZCompleteCallback
& callback
) {
308 Send(kGetPanTiltCommand
,
309 base::Bind(&ViscaWebcam::OnInquiryCompleted
,
310 weak_ptr_factory_
.GetWeakPtr(), INQUIRY_PAN
, callback
));
313 void ViscaWebcam::GetTilt(const GetPTZCompleteCallback
& callback
) {
314 Send(kGetPanTiltCommand
,
315 base::Bind(&ViscaWebcam::OnInquiryCompleted
,
316 weak_ptr_factory_
.GetWeakPtr(), INQUIRY_TILT
, callback
));
319 void ViscaWebcam::GetZoom(const GetPTZCompleteCallback
& callback
) {
320 Send(kGetZoomCommand
,
321 base::Bind(&ViscaWebcam::OnInquiryCompleted
,
322 weak_ptr_factory_
.GetWeakPtr(), INQUIRY_ZOOM
, callback
));
325 void ViscaWebcam::SetPan(int value
,
327 const SetPTZCompleteCallback
& callback
) {
328 pan_speed
= std::min(pan_speed
, MAX_PAN_SPEED
);
329 pan_speed
= pan_speed
> 0 ? pan_speed
: MAX_PAN_SPEED
/ 2;
331 std::vector
<char> command
= kSetPanTiltCommand
;
332 command
[4] |= pan_speed
;
333 command
[5] |= (MAX_TILT_SPEED
/ 2);
334 command
[6] |= ((pan_
& 0xF000) >> 12);
335 command
[7] |= ((pan_
& 0x0F00) >> 8);
336 command
[8] |= ((pan_
& 0x00F0) >> 4);
337 command
[9] |= (pan_
& 0x000F);
338 command
[10] |= ((tilt_
& 0xF000) >> 12);
339 command
[11] |= ((tilt_
& 0x0F00) >> 8);
340 command
[12] |= ((tilt_
& 0x00F0) >> 4);
341 command
[13] |= (tilt_
& 0x000F);
342 Send(command
, base::Bind(&ViscaWebcam::OnCommandCompleted
,
343 weak_ptr_factory_
.GetWeakPtr(), callback
));
346 void ViscaWebcam::SetTilt(int value
,
348 const SetPTZCompleteCallback
& callback
) {
349 tilt_speed
= std::min(tilt_speed
, MAX_TILT_SPEED
);
350 tilt_speed
= tilt_speed
> 0 ? tilt_speed
: MAX_TILT_SPEED
/ 2;
352 std::vector
<char> command
= kSetPanTiltCommand
;
353 command
[4] |= (MAX_PAN_SPEED
/ 2);
354 command
[5] |= tilt_speed
;
355 command
[6] |= ((pan_
& 0xF000) >> 12);
356 command
[7] |= ((pan_
& 0x0F00) >> 8);
357 command
[8] |= ((pan_
& 0x00F0) >> 4);
358 command
[9] |= (pan_
& 0x000F);
359 command
[10] |= ((tilt_
& 0xF000) >> 12);
360 command
[11] |= ((tilt_
& 0x0F00) >> 8);
361 command
[12] |= ((tilt_
& 0x00F0) >> 4);
362 command
[13] |= (tilt_
& 0x000F);
363 Send(command
, base::Bind(&ViscaWebcam::OnCommandCompleted
,
364 weak_ptr_factory_
.GetWeakPtr(), callback
));
367 void ViscaWebcam::SetZoom(int value
, const SetPTZCompleteCallback
& callback
) {
368 std::vector
<char> command
= kSetZoomCommand
;
369 command
[4] |= ((value
& 0xF000) >> 12);
370 command
[5] |= ((value
& 0x0F00) >> 8);
371 command
[6] |= ((value
& 0x00F0) >> 4);
372 command
[7] |= (value
& 0x000F);
373 Send(command
, base::Bind(&ViscaWebcam::OnCommandCompleted
,
374 weak_ptr_factory_
.GetWeakPtr(), callback
));
377 void ViscaWebcam::SetPanDirection(PanDirection direction
,
379 const SetPTZCompleteCallback
& callback
) {
380 pan_speed
= std::min(pan_speed
, MAX_PAN_SPEED
);
381 pan_speed
= pan_speed
> 0 ? pan_speed
: MAX_PAN_SPEED
/ 2;
382 std::vector
<char> command
= kPTStopCommand
;
387 command
= kPTRightCommand
;
388 command
[4] |= pan_speed
;
389 command
[5] |= (MAX_TILT_SPEED
/ 2);
392 command
= kPTLeftCommand
;
393 command
[4] |= pan_speed
;
394 command
[5] |= (MAX_TILT_SPEED
/ 2);
397 Send(command
, base::Bind(&ViscaWebcam::OnCommandCompleted
,
398 weak_ptr_factory_
.GetWeakPtr(), callback
));
401 void ViscaWebcam::SetTiltDirection(TiltDirection direction
,
403 const SetPTZCompleteCallback
& callback
) {
404 tilt_speed
= std::min(tilt_speed
, MAX_TILT_SPEED
);
405 tilt_speed
= tilt_speed
> 0 ? tilt_speed
: MAX_TILT_SPEED
/ 2;
406 std::vector
<char> command
= kPTStopCommand
;
411 command
= kPTUpCommand
;
412 command
[4] |= (MAX_PAN_SPEED
/ 2);
413 command
[5] |= tilt_speed
;
416 command
= kPTDownCommand
;
417 command
[4] |= (MAX_PAN_SPEED
/ 2);
418 command
[5] |= tilt_speed
;
421 Send(command
, base::Bind(&ViscaWebcam::OnCommandCompleted
,
422 weak_ptr_factory_
.GetWeakPtr(), callback
));
425 void ViscaWebcam::Reset(bool pan
,
428 const SetPTZCompleteCallback
& callback
) {
429 // pan and tilt are always reset together in Visca Webcams.
431 Send(kResetPanTiltCommand
,
432 base::Bind(&ViscaWebcam::OnCommandCompleted
,
433 weak_ptr_factory_
.GetWeakPtr(), callback
));
436 const int default_zoom
= 1;
437 SetZoom(default_zoom
, callback
);
441 } // namespace extensions