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