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.
4 
5 #include "extensions/browser/api/webcam_private/visca_webcam.h"
6 
7 #include <stddef.h>
8 #include <stdint.h>
9 
10 #include <algorithm>
11 
12 #include "base/bind.h"
13 #include "base/stl_util.h"
14 
15 using content::BrowserThread;
16 
17 namespace {
18 
19 // Message terminator:
20 const char kViscaTerminator = 0xFF;
21 
22 // Response types:
23 const char kViscaResponseNetworkChange = 0x38;
24 const char kViscaResponseAck = 0x40;
25 const char kViscaResponseError = 0x60;
26 
27 // The default pan speed is kMaxPanSpeed /2 and the default tilt speed is
28 // kMaxTiltSpeed / 2.
29 const int kMaxPanSpeed = 0x18;
30 const int kMaxTiltSpeed = 0x14;
31 const int kDefaultPanSpeed = 0x18 / 2;
32 const int kDefaultTiltSpeed = 0x14 / 2;
33 
34 // Pan-Tilt-Zoom movement comands from http://www.manualslib.com/manual/...
35 // 557364/Cisco-Precisionhd-1080p12x.html?page=31#manual
36 
37 // Reset the address of each device in the VISCA chain (broadcast). This is used
38 // when resetting the VISCA network.
39 const char kSetAddressCommand[] = {0x88, 0x30, 0x01, 0xFF};
40 
41 // Clear all of the devices, halting any pending commands in the VISCA chain
42 // (broadcast). This is used when resetting the VISCA network.
43 const char kClearAllCommand[] = {0x88, 0x01, 0x00, 0x01, 0xFF};
44 
45 // Command: {0x8X, 0x09, 0x06, 0x12, 0xFF}, X = 1 to 7: target device address.
46 // Response: {0xY0, 0x50, 0x0p, 0x0q, 0x0r, 0x0s, 0x0t, 0x0u, 0x0v, 0x0w, 0xFF},
47 // Y = socket number; pqrs: pan position; tuvw: tilt position.
48 const char kGetPanTiltCommand[] = {0x81, 0x09, 0x06, 0x12, 0xFF};
49 
50 // Command: {0x8X, 0x01, 0x06, 0x02, 0x0p, 0x0t, 0x0q, 0x0r, 0x0s, 0x0u, 0x0v,
51 // 0x0w, 0x0y, 0x0z, 0xFF}, X = 1 to 7: target device address; p = pan speed;
52 // t = tilt speed; qrsu = pan position; vwyz = tilt position.
53 const char kSetPanTiltCommand[] = {0x81, 0x01, 0x06, 0x02, 0x00,
54                                    0x00, 0x00, 0x00, 0x00, 0x00,
55                                    0x00, 0x00, 0x00, 0x00, 0xFF};
56 
57 // Command: {0x8X, 0x01, 0x06, 0x05, 0xFF}, X = 1 to 7: target device address.
58 const char kResetPanTiltCommand[] = {0x81, 0x01, 0x06, 0x05, 0xFF};
59 
60 // Command: {0x8X, 0x09, 0x04, 0x47, 0xFF}, X = 1 to 7: target device address.
61 // Response: {0xY0, 0x50, 0x0p, 0x0q, 0x0r, 0x0s, 0xFF}, Y = socket number;
62 // pqrs: zoom position.
63 const char kGetZoomCommand[] = {0x81, 0x09, 0x04, 0x47, 0xFF};
64 
65 // Command: {0x8X, 0x01, 0x04, 0x47, 0x0p, 0x0q, 0x0r, 0x0s, 0xFF}, X = 1 to 7:
66 // target device address; pqrs: zoom position;
67 const char kSetZoomCommand[] = {0x81, 0x01, 0x04, 0x47, 0x00,
68                                 0x00, 0x00, 0x00, 0xFF};
69 
70 // Command: {0x8X, 0x01, 0x04, 0x38, 0x02, 0xFF}, X = 1 to 7: target device
71 // address.
72 const char kSetAutoFocusCommand[] = {0x81, 0x01, 0x04, 0x38, 0x02, 0xFF};
73 
74 // Command: {0x8X, 0x01, 0x04, 0x38, 0x03, 0xFF}, X = 1 to 7: target device
75 // address.
76 const char kSetManualFocusCommand[] = {0x81, 0x01, 0x04, 0x38, 0x03, 0xFF};
77 
78 // Command: {0x8X, 0x09, 0x04, 0x48, 0xFF}, X = 1 to 7: target device address.
79 // Response: {0xY0, 0x50, 0x0p, 0x0q, 0x0r, 0x0s, 0xFF}, Y = socket number;
80 // pqrs: focus position.
81 const char kGetFocusCommand[] = {0x81, 0x09, 0x04, 0x48, 0xFF};
82 
83 // Command: {0x8X, 0x01, 0x04, 0x48, 0x0p, 0x0q, 0x0r, 0x0s, 0xFF}, X = 1 to 7:
84 // target device address; pqrs: focus position;
85 const char kSetFocusCommand[] = {0x81, 0x01, 0x04, 0x48, 0x00,
86                                  0x00, 0x00, 0x00, 0xFF};
87 
88 // Command: {0x8X, 0x01, 0x06, 0x01, 0x0p, 0x0t, 0x03, 0x01, 0xFF}, X = 1 to 7:
89 // target device address; p: pan speed; t: tilt speed.
90 const char kPTUpCommand[] = {0x81, 0x01, 0x06, 0x01, 0x00,
91                              0x00, 0x03, 0x01, 0xFF};
92 
93 // Command: {0x8X, 0x01, 0x06, 0x01, 0x0p, 0x0t, 0x03, 0x02, 0xFF}, X = 1 to 7:
94 // target device address; p: pan speed; t: tilt speed.
95 const char kPTDownCommand[] = {0x81, 0x01, 0x06, 0x01, 0x00,
96                                0x00, 0x03, 0x02, 0xFF};
97 
98 // Command: {0x8X, 0x01, 0x06, 0x01, 0x0p, 0x0t, 0x0, 0x03, 0xFF}, X = 1 to 7:
99 // target device address; p: pan speed; t: tilt speed.
100 const char kPTLeftCommand[] = {0x81, 0x01, 0x06, 0x01, 0x00,
101                                0x00, 0x01, 0x03, 0xFF};
102 
103 // Command: {0x8X, 0x01, 0x06, 0x01, 0x0p, 0x0t, 0x02, 0x03, 0xFF}, X = 1 to 7:
104 // target device address; p: pan speed; t: tilt speed.
105 const char kPTRightCommand[] = {0x81, 0x01, 0x06, 0x01, 0x00,
106                                 0x00, 0x02, 0x03, 0xFF};
107 
108 // Command: {0x8X, 0x01, 0x06, 0x01, 0x03, 0x03, 0x03, 0x03, 0xFF}, X = 1 to 7:
109 // target device address.
110 const char kPTStopCommand[] = {0x81, 0x01, 0x06, 0x01, 0x03,
111                                0x03, 0x03, 0x03, 0xFF};
112 
113 #define CHAR_VECTOR_FROM_ARRAY(array) \
114   std::vector<char>(array, array + base::size(array))
115 
ShiftResponseLowerBits(char c,size_t shift)116 int ShiftResponseLowerBits(char c, size_t shift) {
117   return static_cast<int>(c & 0x0F) << shift;
118 }
119 
CanBuildResponseInt(const std::vector<char> & response,size_t start_index)120 bool CanBuildResponseInt(const std::vector<char>& response,
121                          size_t start_index) {
122   return response.size() >= start_index + 4;
123 }
124 
BuildResponseInt(const std::vector<char> & response,size_t start_index)125 int BuildResponseInt(const std::vector<char>& response, size_t start_index) {
126   return ShiftResponseLowerBits(response[start_index], 12) +
127          ShiftResponseLowerBits(response[start_index + 1], 8) +
128          ShiftResponseLowerBits(response[start_index + 2], 4) +
129          ShiftResponseLowerBits(response[start_index + 3], 0);
130 }
131 
ResponseToCommand(std::vector<char> * command,size_t start_index,uint16_t response)132 void ResponseToCommand(std::vector<char>* command,
133                        size_t start_index,
134                        uint16_t response) {
135   DCHECK(command);
136   std::vector<char>& command_ref = *command;
137   command_ref[start_index] |= ((response >> 12) & 0x0F);
138   command_ref[start_index + 1] |= ((response >> 8) & 0x0F);
139   command_ref[start_index + 2] |= ((response >> 4 & 0x0F));
140   command_ref[start_index + 3] |= (response & 0x0F);
141 }
142 
CalculateSpeed(int desired_speed,int max_speed,int default_speed)143 int CalculateSpeed(int desired_speed, int max_speed, int default_speed) {
144   int speed = std::min(desired_speed, max_speed);
145   return speed > 0 ? speed : default_speed;
146 }
147 
GetPositiveValue(int value)148 int GetPositiveValue(int value) {
149   return value < 0x8000 ? value : value - 0xFFFF;
150 }
151 
152 }  // namespace
153 
154 namespace extensions {
155 
156 ViscaWebcam::ViscaWebcam() = default;
157 
158 ViscaWebcam::~ViscaWebcam() = default;
159 
Open(const std::string & extension_id,api::SerialPortManager * port_manager,const std::string & path,const OpenCompleteCallback & open_callback)160 void ViscaWebcam::Open(const std::string& extension_id,
161                        api::SerialPortManager* port_manager,
162                        const std::string& path,
163                        const OpenCompleteCallback& open_callback) {
164   api::serial::ConnectionOptions options;
165 
166   // Set the receive buffer size to receive the response data 1 by 1.
167   options.buffer_size.reset(new int(1));
168   options.persistent.reset(new bool(false));
169   options.bitrate.reset(new int(9600));
170   options.cts_flow_control.reset(new bool(false));
171   // Enable send and receive timeout error.
172   options.receive_timeout.reset(new int(3000));
173   options.send_timeout.reset(new int(3000));
174   options.data_bits = api::serial::DATA_BITS_EIGHT;
175   options.parity_bit = api::serial::PARITY_BIT_NO;
176   options.stop_bits = api::serial::STOP_BITS_ONE;
177 
178   serial_connection_ = std::make_unique<SerialConnection>(extension_id);
179   serial_connection_->Open(
180       port_manager, path, options,
181       base::BindOnce(&ViscaWebcam::OnConnected, base::Unretained(this),
182                      open_callback));
183 }
184 
OnConnected(const OpenCompleteCallback & open_callback,bool success)185 void ViscaWebcam::OnConnected(const OpenCompleteCallback& open_callback,
186                               bool success) {
187   if (!success) {
188     open_callback.Run(success);
189     return;
190   }
191 
192   Send(CHAR_VECTOR_FROM_ARRAY(kSetAddressCommand),
193        base::Bind(&ViscaWebcam::OnAddressSetCompleted, base::Unretained(this),
194                   open_callback));
195 }
196 
OnAddressSetCompleted(const OpenCompleteCallback & open_callback,bool success,const std::vector<char> & response)197 void ViscaWebcam::OnAddressSetCompleted(
198     const OpenCompleteCallback& open_callback,
199     bool success,
200     const std::vector<char>& response) {
201   commands_.pop_front();
202   if (!success) {
203     open_callback.Run(success);
204     return;
205   }
206 
207   Send(CHAR_VECTOR_FROM_ARRAY(kClearAllCommand),
208        base::Bind(&ViscaWebcam::OnClearAllCompleted, base::Unretained(this),
209                   open_callback));
210 }
211 
OnClearAllCompleted(const OpenCompleteCallback & open_callback,bool success,const std::vector<char> & response)212 void ViscaWebcam::OnClearAllCompleted(const OpenCompleteCallback& open_callback,
213                                       bool success,
214                                       const std::vector<char>& response) {
215   commands_.pop_front();
216   open_callback.Run(success);
217 }
218 
Send(const std::vector<char> & command,const CommandCompleteCallback & callback)219 void ViscaWebcam::Send(const std::vector<char>& command,
220                        const CommandCompleteCallback& callback) {
221   commands_.push_back(std::make_pair(command, callback));
222   // If this is the only command in the queue, send it now.
223   if (commands_.size() == 1) {
224     serial_connection_->Send(
225         std::vector<uint8_t>(command.begin(), command.end()),
226         base::BindOnce(&ViscaWebcam::OnSendCompleted, base::Unretained(this),
227                        callback));
228   }
229 }
230 
OnSendCompleted(const CommandCompleteCallback & callback,uint32_t bytes_sent,api::serial::SendError error)231 void ViscaWebcam::OnSendCompleted(const CommandCompleteCallback& callback,
232                                   uint32_t bytes_sent,
233                                   api::serial::SendError error) {
234   // TODO(xdai): Check |bytes_sent|?
235   if (error == api::serial::SEND_ERROR_NONE) {
236     serial_connection_->StartPolling(base::BindRepeating(
237         &ViscaWebcam::OnReceiveEvent, base::Unretained(this), callback));
238   } else {
239     callback.Run(false, std::vector<char>());
240   }
241 }
242 
OnReceiveEvent(const CommandCompleteCallback & callback,std::vector<uint8_t> data,api::serial::ReceiveError error)243 void ViscaWebcam::OnReceiveEvent(const CommandCompleteCallback& callback,
244                                  std::vector<uint8_t> data,
245                                  api::serial::ReceiveError error) {
246   data_buffer_.insert(data_buffer_.end(), data.begin(), data.end());
247 
248   if (error != api::serial::RECEIVE_ERROR_NONE || data_buffer_.empty()) {
249     // Clear |data_buffer_|.
250     std::vector<char> response;
251     response.swap(data_buffer_);
252     serial_connection_->SetPaused(true);
253     callback.Run(false, response);
254     return;
255   }
256 
257   // Success case. If waiting for more data, then loop until encounter the
258   // terminator.
259   if (data_buffer_.back() != kViscaTerminator)
260     return;
261 
262   // Success case, and a complete response has been received.
263   // Clear |data_buffer_|.
264   std::vector<char> response;
265   response.swap(data_buffer_);
266 
267   if (response.size() < 2 ||
268       (static_cast<int>(response[1]) & 0xF0) == kViscaResponseError) {
269     serial_connection_->SetPaused(true);
270     callback.Run(false, response);
271   } else if ((static_cast<int>(response[1]) & 0xF0) != kViscaResponseAck &&
272              (static_cast<int>(response[1]) & 0xFF) !=
273                  kViscaResponseNetworkChange) {
274     serial_connection_->SetPaused(true);
275     callback.Run(true, response);
276   }
277 }
278 
OnCommandCompleted(const SetPTZCompleteCallback & callback,bool success,const std::vector<char> & response)279 void ViscaWebcam::OnCommandCompleted(const SetPTZCompleteCallback& callback,
280                                      bool success,
281                                      const std::vector<char>& response) {
282   // TODO(xdai): Error handling according to |response|.
283   callback.Run(success);
284   ProcessNextCommand();
285 }
286 
OnInquiryCompleted(InquiryType type,const GetPTZCompleteCallback & callback,bool success,const std::vector<char> & response)287 void ViscaWebcam::OnInquiryCompleted(InquiryType type,
288                                      const GetPTZCompleteCallback& callback,
289                                      bool success,
290                                      const std::vector<char>& response) {
291   if (!success) {
292     callback.Run(false, 0 /* value */, 0 /* min_value */, 0 /* max_value */);
293     ProcessNextCommand();
294     return;
295   }
296 
297   bool is_valid_response = false;
298   switch (type) {
299     case INQUIRY_PAN:
300       is_valid_response = CanBuildResponseInt(response, 2);
301       break;
302     case INQUIRY_TILT:
303       is_valid_response = CanBuildResponseInt(response, 6);
304       break;
305     case INQUIRY_ZOOM:
306       is_valid_response = CanBuildResponseInt(response, 2);
307       break;
308     case INQUIRY_FOCUS:
309       is_valid_response = CanBuildResponseInt(response, 2);
310       break;
311   }
312   if (!is_valid_response) {
313     callback.Run(false, 0 /* value */, 0 /* min_value */, 0 /* max_value */);
314     ProcessNextCommand();
315     return;
316   }
317 
318   int value = 0;
319   switch (type) {
320     case INQUIRY_PAN:
321       // See kGetPanTiltCommand for the format of response.
322       pan_ = BuildResponseInt(response, 2);
323       value = GetPositiveValue(pan_);
324       break;
325     case INQUIRY_TILT:
326       // See kGetPanTiltCommand for the format of response.
327       tilt_ = BuildResponseInt(response, 6);
328       value = GetPositiveValue(tilt_);
329       break;
330     case INQUIRY_ZOOM:
331       // See kGetZoomCommand for the format of response.
332       value = BuildResponseInt(response, 2);
333       break;
334     case INQUIRY_FOCUS:
335       // See kGetFocusCommand for the format of response.
336       value = BuildResponseInt(response, 2);
337       break;
338   }
339   // TODO(pbos): Add support for valid ranges.
340   callback.Run(true, value, 0, 0);
341   ProcessNextCommand();
342 }
343 
ProcessNextCommand()344 void ViscaWebcam::ProcessNextCommand() {
345   commands_.pop_front();
346 
347   if (commands_.empty())
348     return;
349 
350   // If there are pending commands, process the next one.
351   const std::vector<char> next_command = commands_.front().first;
352   const CommandCompleteCallback next_callback = commands_.front().second;
353   serial_connection_->Send(
354       std::vector<uint8_t>(next_command.begin(), next_command.end()),
355       base::BindOnce(&ViscaWebcam::OnSendCompleted, base::Unretained(this),
356                      next_callback));
357 }
358 
GetPan(const GetPTZCompleteCallback & callback)359 void ViscaWebcam::GetPan(const GetPTZCompleteCallback& callback) {
360   Send(CHAR_VECTOR_FROM_ARRAY(kGetPanTiltCommand),
361        base::Bind(&ViscaWebcam::OnInquiryCompleted, base::Unretained(this),
362                   INQUIRY_PAN, callback));
363 }
364 
GetTilt(const GetPTZCompleteCallback & callback)365 void ViscaWebcam::GetTilt(const GetPTZCompleteCallback& callback) {
366   Send(CHAR_VECTOR_FROM_ARRAY(kGetPanTiltCommand),
367        base::Bind(&ViscaWebcam::OnInquiryCompleted, base::Unretained(this),
368                   INQUIRY_TILT, callback));
369 }
370 
GetZoom(const GetPTZCompleteCallback & callback)371 void ViscaWebcam::GetZoom(const GetPTZCompleteCallback& callback) {
372   Send(CHAR_VECTOR_FROM_ARRAY(kGetZoomCommand),
373        base::Bind(&ViscaWebcam::OnInquiryCompleted, base::Unretained(this),
374                   INQUIRY_ZOOM, callback));
375 }
376 
GetFocus(const GetPTZCompleteCallback & callback)377 void ViscaWebcam::GetFocus(const GetPTZCompleteCallback& callback) {
378   Send(CHAR_VECTOR_FROM_ARRAY(kGetFocusCommand),
379        base::Bind(&ViscaWebcam::OnInquiryCompleted, base::Unretained(this),
380                   INQUIRY_FOCUS, callback));
381 }
382 
SetPan(int value,int pan_speed,const SetPTZCompleteCallback & callback)383 void ViscaWebcam::SetPan(int value,
384                          int pan_speed,
385                          const SetPTZCompleteCallback& callback) {
386   int actual_pan_speed =
387       CalculateSpeed(pan_speed, kMaxPanSpeed, kDefaultPanSpeed);
388   pan_ = value;
389 
390   std::vector<char> command = CHAR_VECTOR_FROM_ARRAY(kSetPanTiltCommand);
391   command[4] |= actual_pan_speed;
392   command[5] |= kDefaultTiltSpeed;
393   ResponseToCommand(&command, 6, static_cast<uint16_t>(pan_));
394   ResponseToCommand(&command, 10, static_cast<uint16_t>(tilt_));
395   Send(command, base::Bind(&ViscaWebcam::OnCommandCompleted,
396                            base::Unretained(this), callback));
397 }
398 
SetTilt(int value,int tilt_speed,const SetPTZCompleteCallback & callback)399 void ViscaWebcam::SetTilt(int value,
400                           int tilt_speed,
401                           const SetPTZCompleteCallback& callback) {
402   int actual_tilt_speed =
403       CalculateSpeed(tilt_speed, kMaxTiltSpeed, kDefaultTiltSpeed);
404   tilt_ = value;
405 
406   std::vector<char> command = CHAR_VECTOR_FROM_ARRAY(kSetPanTiltCommand);
407   command[4] |= kDefaultPanSpeed;
408   command[5] |= actual_tilt_speed;
409   ResponseToCommand(&command, 6, static_cast<uint16_t>(pan_));
410   ResponseToCommand(&command, 10, static_cast<uint16_t>(tilt_));
411   Send(command, base::Bind(&ViscaWebcam::OnCommandCompleted,
412                            base::Unretained(this), callback));
413 }
414 
SetZoom(int value,const SetPTZCompleteCallback & callback)415 void ViscaWebcam::SetZoom(int value, const SetPTZCompleteCallback& callback) {
416   int actual_value = std::max(value, 0);
417   std::vector<char> command = CHAR_VECTOR_FROM_ARRAY(kSetZoomCommand);
418   ResponseToCommand(&command, 4, actual_value);
419   Send(command, base::Bind(&ViscaWebcam::OnCommandCompleted,
420                            base::Unretained(this), callback));
421 }
422 
SetFocus(int value,const SetPTZCompleteCallback & callback)423 void ViscaWebcam::SetFocus(int value, const SetPTZCompleteCallback& callback) {
424   int actual_value = std::max(value, 0);
425   std::vector<char> command = CHAR_VECTOR_FROM_ARRAY(kSetFocusCommand);
426   ResponseToCommand(&command, 4, actual_value);
427   Send(command, base::Bind(&ViscaWebcam::OnCommandCompleted,
428                            base::Unretained(this), callback));
429 }
430 
SetAutofocusState(AutofocusState state,const SetPTZCompleteCallback & callback)431 void ViscaWebcam::SetAutofocusState(AutofocusState state,
432                                     const SetPTZCompleteCallback& callback) {
433   std::vector<char> command;
434   if (state == AUTOFOCUS_ON) {
435     command = CHAR_VECTOR_FROM_ARRAY(kSetAutoFocusCommand);
436   } else {
437     command = CHAR_VECTOR_FROM_ARRAY(kSetManualFocusCommand);
438   }
439   Send(command, base::Bind(&ViscaWebcam::OnCommandCompleted,
440                            base::Unretained(this), callback));
441 }
442 
SetPanDirection(PanDirection direction,int pan_speed,const SetPTZCompleteCallback & callback)443 void ViscaWebcam::SetPanDirection(PanDirection direction,
444                                   int pan_speed,
445                                   const SetPTZCompleteCallback& callback) {
446   int actual_pan_speed =
447       CalculateSpeed(pan_speed, kMaxPanSpeed, kDefaultPanSpeed);
448   std::vector<char> command = CHAR_VECTOR_FROM_ARRAY(kPTStopCommand);
449   switch (direction) {
450     case PAN_STOP:
451       break;
452     case PAN_RIGHT:
453       command = CHAR_VECTOR_FROM_ARRAY(kPTRightCommand);
454       command[4] |= actual_pan_speed;
455       command[5] |= kDefaultTiltSpeed;
456       break;
457     case PAN_LEFT:
458       command = CHAR_VECTOR_FROM_ARRAY(kPTLeftCommand);
459       command[4] |= actual_pan_speed;
460       command[5] |= kDefaultTiltSpeed;
461       break;
462   }
463   Send(command, base::Bind(&ViscaWebcam::OnCommandCompleted,
464                            base::Unretained(this), callback));
465 }
466 
SetTiltDirection(TiltDirection direction,int tilt_speed,const SetPTZCompleteCallback & callback)467 void ViscaWebcam::SetTiltDirection(TiltDirection direction,
468                                    int tilt_speed,
469                                    const SetPTZCompleteCallback& callback) {
470   int actual_tilt_speed =
471       CalculateSpeed(tilt_speed, kMaxTiltSpeed, kDefaultTiltSpeed);
472   std::vector<char> command = CHAR_VECTOR_FROM_ARRAY(kPTStopCommand);
473   switch (direction) {
474     case TILT_STOP:
475       break;
476     case TILT_UP:
477       command = CHAR_VECTOR_FROM_ARRAY(kPTUpCommand);
478       command[4] |= kDefaultPanSpeed;
479       command[5] |= actual_tilt_speed;
480       break;
481     case TILT_DOWN:
482       command = CHAR_VECTOR_FROM_ARRAY(kPTDownCommand);
483       command[4] |= kDefaultPanSpeed;
484       command[5] |= actual_tilt_speed;
485       break;
486   }
487   Send(command, base::Bind(&ViscaWebcam::OnCommandCompleted,
488                            base::Unretained(this), callback));
489 }
490 
Reset(bool pan,bool tilt,bool zoom,const SetPTZCompleteCallback & callback)491 void ViscaWebcam::Reset(bool pan,
492                         bool tilt,
493                         bool zoom,
494                         const SetPTZCompleteCallback& callback) {
495   // pan and tilt are always reset together in Visca Webcams.
496   if (pan || tilt) {
497     Send(CHAR_VECTOR_FROM_ARRAY(kResetPanTiltCommand),
498          base::Bind(&ViscaWebcam::OnCommandCompleted, base::Unretained(this),
499                     callback));
500   }
501   if (zoom) {
502     // Set the default zoom value to 100 to be consistent with V4l2 webcam.
503     const int kDefaultZoom = 100;
504     SetZoom(kDefaultZoom, callback);
505   }
506 }
507 
SetHome(const SetPTZCompleteCallback & callback)508 void ViscaWebcam::SetHome(const SetPTZCompleteCallback& callback) {}
509 
RestoreCameraPreset(int preset_number,const SetPTZCompleteCallback & callback)510 void ViscaWebcam::RestoreCameraPreset(int preset_number,
511                                       const SetPTZCompleteCallback& callback) {}
SetCameraPreset(int preset_number,const SetPTZCompleteCallback & callback)512 void ViscaWebcam::SetCameraPreset(int preset_number,
513                                   const SetPTZCompleteCallback& callback) {}
514 
OpenForTesting(std::unique_ptr<SerialConnection> serial_connection)515 void ViscaWebcam::OpenForTesting(
516     std::unique_ptr<SerialConnection> serial_connection) {
517   serial_connection_ = std::move(serial_connection);
518 }
519 
GetSerialConnectionForTesting()520 SerialConnection* ViscaWebcam::GetSerialConnectionForTesting() {
521   return serial_connection_.get();
522 }
523 
524 }  // namespace extensions
525