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 // Enable send and receive timeout error.
127 options
.receive_timeout
.reset(new int(3000));
128 options
.send_timeout
.reset(new int(3000));
129 options
.data_bits
= api::serial::DATA_BITS_EIGHT
;
130 options
.parity_bit
= api::serial::PARITY_BIT_NO
;
131 options
.stop_bits
= api::serial::STOP_BITS_ONE
;
133 serial_connection_
.reset(new SerialConnection(path_
, extension_id_
));
134 serial_connection_
->Open(
135 options
, base::Bind(&ViscaWebcam::OnConnected
,
136 weak_ptr_factory_
.GetWeakPtr(), open_callback
));
139 void ViscaWebcam::OnConnected(const OpenCompleteCallback
& open_callback
,
142 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
143 base::Bind(open_callback
, false));
145 Send(kSetAddressCommand
,
146 base::Bind(&ViscaWebcam::OnAddressSetCompleted
,
147 weak_ptr_factory_
.GetWeakPtr(), open_callback
));
151 void ViscaWebcam::OnAddressSetCompleted(
152 const OpenCompleteCallback
& open_callback
,
154 const std::vector
<char>& response
) {
155 commands_
.pop_front();
157 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
158 base::Bind(open_callback
, false));
160 Send(kClearAllCommand
,
161 base::Bind(&ViscaWebcam::OnClearAllCompleted
,
162 weak_ptr_factory_
.GetWeakPtr(), open_callback
));
166 void ViscaWebcam::OnClearAllCompleted(const OpenCompleteCallback
& open_callback
,
168 const std::vector
<char>& response
) {
169 commands_
.pop_front();
171 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
172 base::Bind(open_callback
, false));
174 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
175 base::Bind(open_callback
, true));
179 void ViscaWebcam::Send(const std::vector
<char>& command
,
180 const CommandCompleteCallback
& callback
) {
181 commands_
.push_back(std::make_pair(command
, callback
));
182 // If this is the only command in the queue, send it now.
183 if (commands_
.size() == 1) {
184 serial_connection_
->Send(
185 command
, base::Bind(&ViscaWebcam::OnSendCompleted
,
186 weak_ptr_factory_
.GetWeakPtr(), callback
));
190 void ViscaWebcam::OnSendCompleted(const CommandCompleteCallback
& callback
,
192 api::serial::SendError error
) {
193 if (error
== api::serial::SEND_ERROR_NONE
) {
194 ReceiveLoop(callback
);
196 const std::vector
<char> response
;
197 callback
.Run(false, response
);
201 void ViscaWebcam::ReceiveLoop(const CommandCompleteCallback
& callback
) {
202 serial_connection_
->Receive(base::Bind(&ViscaWebcam::OnReceiveCompleted
,
203 weak_ptr_factory_
.GetWeakPtr(),
207 void ViscaWebcam::OnReceiveCompleted(const CommandCompleteCallback
& callback
,
208 const std::vector
<char>& data
,
209 api::serial::ReceiveError error
) {
210 data_buffer_
.insert(data_buffer_
.end(), data
.begin(), data
.end());
212 if (error
== api::serial::RECEIVE_ERROR_NONE
) {
213 // Loop until encounter the terminator.
214 if (int(data_buffer_
.back()) != VISCA_TERMINATOR
) {
215 base::MessageLoop::current()->PostTask(
216 FROM_HERE
, base::Bind(&ViscaWebcam::ReceiveLoop
,
217 weak_ptr_factory_
.GetWeakPtr(), callback
));
219 // Clear |data_buffer_|.
220 std::vector
<char> response
;
221 response
.swap(data_buffer_
);
223 if ((int(response
[1]) & 0xF0) == VISCA_RESPONSE_ERROR
) {
224 callback
.Run(false, response
);
225 } else if ((int(response
[1]) & 0xF0) != VISCA_RESPONSE_ACK
&&
226 (int(response
[1]) & 0xFF) != VISCA_RESPONSE_NETWORK_CHANGE
) {
227 callback
.Run(true, response
);
229 base::MessageLoop::current()->PostTask(
230 FROM_HERE
, base::Bind(&ViscaWebcam::ReceiveLoop
,
231 weak_ptr_factory_
.GetWeakPtr(), callback
));
235 // Clear |data_buffer_|.
236 std::vector
<char> response
;
237 response
.swap(data_buffer_
);
238 callback
.Run(false, response
);
242 void ViscaWebcam::OnCommandCompleted(const SetPTZCompleteCallback
& callback
,
244 const std::vector
<char>& response
) {
245 // TODO(xdai): Error handling according to |response|.
246 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
247 base::Bind(callback
, success
));
248 commands_
.pop_front();
250 // If there are pending commands, process the next one.
251 if (!commands_
.empty()) {
252 const std::vector
<char> next_command
= commands_
.front().first
;
253 const CommandCompleteCallback next_callback
= commands_
.front().second
;
254 serial_connection_
->Send(
256 base::Bind(&ViscaWebcam::OnSendCompleted
,
257 weak_ptr_factory_
.GetWeakPtr(), next_callback
));
261 void ViscaWebcam::OnInquiryCompleted(InquiryType type
,
262 const GetPTZCompleteCallback
& callback
,
264 const std::vector
<char>& response
) {
267 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
268 base::Bind(callback
, false, value
));
272 // See kGetPanTiltCommand for the format of response.
273 pan_
= ((int(response
[2]) & 0x0F) << 12) +
274 ((int(response
[3]) & 0x0F) << 8) +
275 ((int(response
[4]) & 0x0F) << 4) + (int(response
[5]) & 0x0F);
276 value
= pan_
< 0x8000 ? pan_
: pan_
- 0xFFFF;
279 // See kGetPanTiltCommand for the format of response.
280 tilt_
= ((int(response
[6]) & 0x0F) << 12) +
281 ((int(response
[7]) & 0x0F) << 8) +
282 ((int(response
[8]) & 0x0F) << 4) + (int(response
[9]) & 0x0F);
283 value
= tilt_
< 0x8000 ? tilt_
: tilt_
- 0xFFFF;
286 // See kGetZoomCommand for the format of response.
287 value
= ((int(response
[2]) & 0x0F) << 12) +
288 ((int(response
[3]) & 0x0F) << 8) +
289 ((int(response
[4]) & 0x0F) << 4) + (int(response
[5]) & 0x0F);
292 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
293 base::Bind(callback
, true, value
));
295 commands_
.pop_front();
297 // If there are pending commands, process the next one.
298 if (!commands_
.empty()) {
299 const std::vector
<char> next_command
= commands_
.front().first
;
300 const CommandCompleteCallback next_callback
= commands_
.front().second
;
301 serial_connection_
->Send(
303 base::Bind(&ViscaWebcam::OnSendCompleted
,
304 weak_ptr_factory_
.GetWeakPtr(), next_callback
));
308 void ViscaWebcam::GetPan(const GetPTZCompleteCallback
& callback
) {
309 Send(kGetPanTiltCommand
,
310 base::Bind(&ViscaWebcam::OnInquiryCompleted
,
311 weak_ptr_factory_
.GetWeakPtr(), INQUIRY_PAN
, callback
));
314 void ViscaWebcam::GetTilt(const GetPTZCompleteCallback
& callback
) {
315 Send(kGetPanTiltCommand
,
316 base::Bind(&ViscaWebcam::OnInquiryCompleted
,
317 weak_ptr_factory_
.GetWeakPtr(), INQUIRY_TILT
, callback
));
320 void ViscaWebcam::GetZoom(const GetPTZCompleteCallback
& callback
) {
321 Send(kGetZoomCommand
,
322 base::Bind(&ViscaWebcam::OnInquiryCompleted
,
323 weak_ptr_factory_
.GetWeakPtr(), INQUIRY_ZOOM
, callback
));
326 void ViscaWebcam::SetPan(int value
,
328 const SetPTZCompleteCallback
& callback
) {
329 pan_speed
= std::min(pan_speed
, MAX_PAN_SPEED
);
330 pan_speed
= pan_speed
> 0 ? pan_speed
: MAX_PAN_SPEED
/ 2;
332 uint16_t pan
= (uint16_t)pan_
;
333 uint16_t tilt
= (uint16_t)tilt_
;
335 std::vector
<char> command
= kSetPanTiltCommand
;
336 command
[4] |= pan_speed
;
337 command
[5] |= (MAX_TILT_SPEED
/ 2);
338 command
[6] |= ((pan
& 0xF000) >> 12);
339 command
[7] |= ((pan
& 0x0F00) >> 8);
340 command
[8] |= ((pan
& 0x00F0) >> 4);
341 command
[9] |= (pan
& 0x000F);
342 command
[10] |= ((tilt
& 0xF000) >> 12);
343 command
[11] |= ((tilt
& 0x0F00) >> 8);
344 command
[12] |= ((tilt
& 0x00F0) >> 4);
345 command
[13] |= (tilt
& 0x000F);
346 Send(command
, base::Bind(&ViscaWebcam::OnCommandCompleted
,
347 weak_ptr_factory_
.GetWeakPtr(), callback
));
350 void ViscaWebcam::SetTilt(int value
,
352 const SetPTZCompleteCallback
& callback
) {
353 tilt_speed
= std::min(tilt_speed
, MAX_TILT_SPEED
);
354 tilt_speed
= tilt_speed
> 0 ? tilt_speed
: MAX_TILT_SPEED
/ 2;
356 uint16_t pan
= (uint16_t)pan_
;
357 uint16_t tilt
= (uint16_t)tilt_
;
359 std::vector
<char> command
= kSetPanTiltCommand
;
360 command
[4] |= (MAX_PAN_SPEED
/ 2);
361 command
[5] |= tilt_speed
;
362 command
[6] |= ((pan
& 0xF000) >> 12);
363 command
[7] |= ((pan
& 0x0F00) >> 8);
364 command
[8] |= ((pan
& 0x00F0) >> 4);
365 command
[9] |= (pan
& 0x000F);
366 command
[10] |= ((tilt
& 0xF000) >> 12);
367 command
[11] |= ((tilt
& 0x0F00) >> 8);
368 command
[12] |= ((tilt
& 0x00F0) >> 4);
369 command
[13] |= (tilt
& 0x000F);
370 Send(command
, base::Bind(&ViscaWebcam::OnCommandCompleted
,
371 weak_ptr_factory_
.GetWeakPtr(), callback
));
374 void ViscaWebcam::SetZoom(int value
, const SetPTZCompleteCallback
& callback
) {
375 value
= value
> 0 ? value
: 0;
376 std::vector
<char> command
= kSetZoomCommand
;
377 command
[4] |= ((value
& 0xF000) >> 12);
378 command
[5] |= ((value
& 0x0F00) >> 8);
379 command
[6] |= ((value
& 0x00F0) >> 4);
380 command
[7] |= (value
& 0x000F);
381 Send(command
, base::Bind(&ViscaWebcam::OnCommandCompleted
,
382 weak_ptr_factory_
.GetWeakPtr(), callback
));
385 void ViscaWebcam::SetPanDirection(PanDirection direction
,
387 const SetPTZCompleteCallback
& callback
) {
388 pan_speed
= std::min(pan_speed
, MAX_PAN_SPEED
);
389 pan_speed
= pan_speed
> 0 ? pan_speed
: MAX_PAN_SPEED
/ 2;
390 std::vector
<char> command
= kPTStopCommand
;
395 command
= kPTRightCommand
;
396 command
[4] |= pan_speed
;
397 command
[5] |= (MAX_TILT_SPEED
/ 2);
400 command
= kPTLeftCommand
;
401 command
[4] |= pan_speed
;
402 command
[5] |= (MAX_TILT_SPEED
/ 2);
405 Send(command
, base::Bind(&ViscaWebcam::OnCommandCompleted
,
406 weak_ptr_factory_
.GetWeakPtr(), callback
));
409 void ViscaWebcam::SetTiltDirection(TiltDirection direction
,
411 const SetPTZCompleteCallback
& callback
) {
412 tilt_speed
= std::min(tilt_speed
, MAX_TILT_SPEED
);
413 tilt_speed
= tilt_speed
> 0 ? tilt_speed
: MAX_TILT_SPEED
/ 2;
414 std::vector
<char> command
= kPTStopCommand
;
419 command
= kPTUpCommand
;
420 command
[4] |= (MAX_PAN_SPEED
/ 2);
421 command
[5] |= tilt_speed
;
424 command
= kPTDownCommand
;
425 command
[4] |= (MAX_PAN_SPEED
/ 2);
426 command
[5] |= tilt_speed
;
429 Send(command
, base::Bind(&ViscaWebcam::OnCommandCompleted
,
430 weak_ptr_factory_
.GetWeakPtr(), callback
));
433 void ViscaWebcam::Reset(bool pan
,
436 const SetPTZCompleteCallback
& callback
) {
437 // pan and tilt are always reset together in Visca Webcams.
439 Send(kResetPanTiltCommand
,
440 base::Bind(&ViscaWebcam::OnCommandCompleted
,
441 weak_ptr_factory_
.GetWeakPtr(), callback
));
444 // Set the default zoom value to 100 to be consistent with V4l2 webcam.
445 const int default_zoom
= 100;
446 SetZoom(default_zoom
, callback
);
450 } // namespace extensions