Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / extensions / browser / api / webcam_private / visca_webcam.cc
blobb090731b658f0e852e07f5f754e468c8d6eeaae1
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"
7 #include "base/bind.h"
8 #include "base/message_loop/message_loop.h"
9 #include "content/public/browser/browser_thread.h"
11 using content::BrowserThread;
13 namespace {
15 // Message terminator:
16 const char VISCA_TERMINATOR = 0xFF;
18 // Response types:
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};
94 } // namespace
96 namespace extensions {
98 ViscaWebcam::ViscaWebcam(const std::string& path,
99 const std::string& extension_id)
100 : path_(path),
101 extension_id_(extension_id),
102 pan_(0),
103 tilt_(0),
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(),
113 open_callback));
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,
140 bool success) {
141 if (!success) {
142 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
143 base::Bind(open_callback, false));
144 } else {
145 Send(kSetAddressCommand,
146 base::Bind(&ViscaWebcam::OnAddressSetCompleted,
147 weak_ptr_factory_.GetWeakPtr(), open_callback));
151 void ViscaWebcam::OnAddressSetCompleted(
152 const OpenCompleteCallback& open_callback,
153 bool success,
154 const std::vector<char>& response) {
155 commands_.pop_front();
156 if (!success) {
157 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
158 base::Bind(open_callback, false));
159 } else {
160 Send(kClearAllCommand,
161 base::Bind(&ViscaWebcam::OnClearAllCompleted,
162 weak_ptr_factory_.GetWeakPtr(), open_callback));
166 void ViscaWebcam::OnClearAllCompleted(const OpenCompleteCallback& open_callback,
167 bool success,
168 const std::vector<char>& response) {
169 commands_.pop_front();
170 if (!success) {
171 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
172 base::Bind(open_callback, false));
173 } else {
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,
191 int bytes_sent,
192 api::serial::SendError error) {
193 if (error == api::serial::SEND_ERROR_NONE) {
194 ReceiveLoop(callback);
195 } else {
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(),
204 callback));
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));
218 } else {
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);
228 } else {
229 base::MessageLoop::current()->PostTask(
230 FROM_HERE, base::Bind(&ViscaWebcam::ReceiveLoop,
231 weak_ptr_factory_.GetWeakPtr(), callback));
234 } else {
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,
243 bool success,
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(
255 next_command,
256 base::Bind(&ViscaWebcam::OnSendCompleted,
257 weak_ptr_factory_.GetWeakPtr(), next_callback));
261 void ViscaWebcam::OnInquiryCompleted(InquiryType type,
262 const GetPTZCompleteCallback& callback,
263 bool success,
264 const std::vector<char>& response) {
265 int value = 0;
266 if (!success) {
267 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
268 base::Bind(callback, false, value));
269 } else {
270 switch (type) {
271 case INQUIRY_PAN:
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;
277 break;
278 case INQUIRY_TILT:
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;
284 break;
285 case INQUIRY_ZOOM:
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);
290 break;
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(
302 next_command,
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,
327 int pan_speed,
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;
331 pan_ = value;
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,
351 int tilt_speed,
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;
355 tilt_ = value;
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,
386 int pan_speed,
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;
391 switch (direction) {
392 case PAN_STOP:
393 break;
394 case PAN_RIGHT:
395 command = kPTRightCommand;
396 command[4] |= pan_speed;
397 command[5] |= (MAX_TILT_SPEED / 2);
398 break;
399 case PAN_LEFT:
400 command = kPTLeftCommand;
401 command[4] |= pan_speed;
402 command[5] |= (MAX_TILT_SPEED / 2);
403 break;
405 Send(command, base::Bind(&ViscaWebcam::OnCommandCompleted,
406 weak_ptr_factory_.GetWeakPtr(), callback));
409 void ViscaWebcam::SetTiltDirection(TiltDirection direction,
410 int tilt_speed,
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;
415 switch (direction) {
416 case TILT_STOP:
417 break;
418 case TILT_UP:
419 command = kPTUpCommand;
420 command[4] |= (MAX_PAN_SPEED / 2);
421 command[5] |= tilt_speed;
422 break;
423 case TILT_DOWN:
424 command = kPTDownCommand;
425 command[4] |= (MAX_PAN_SPEED / 2);
426 command[5] |= tilt_speed;
427 break;
429 Send(command, base::Bind(&ViscaWebcam::OnCommandCompleted,
430 weak_ptr_factory_.GetWeakPtr(), callback));
433 void ViscaWebcam::Reset(bool pan,
434 bool tilt,
435 bool zoom,
436 const SetPTZCompleteCallback& callback) {
437 // pan and tilt are always reset together in Visca Webcams.
438 if (pan || tilt) {
439 Send(kResetPanTiltCommand,
440 base::Bind(&ViscaWebcam::OnCommandCompleted,
441 weak_ptr_factory_.GetWeakPtr(), callback));
443 if (zoom) {
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