| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "extensions/browser/api/webcam_private/webcam_private_api.h" |
| |
| #include "base/lazy_instance.h" |
| #include "content/public/browser/browser_context.h" |
| #include "content/public/browser/media_device_id.h" |
| #include "content/public/browser/resource_context.h" |
| #include "extensions/browser/api/serial/serial_port_manager.h" |
| #include "extensions/browser/api/webcam_private/v4l2_webcam.h" |
| #include "extensions/browser/api/webcam_private/visca_webcam.h" |
| #include "extensions/browser/process_manager.h" |
| #include "extensions/browser/process_manager_factory.h" |
| #include "extensions/common/api/webcam_private.h" |
| #include "url/origin.h" |
| |
| namespace webcam_private = extensions::api::webcam_private; |
| |
| namespace content { |
| class BrowserContext; |
| } // namespace content |
| |
| namespace { |
| |
| const char kPathInUse[] = "Path in use"; |
| const char kUnknownWebcam[] = "Unknown webcam id"; |
| const char kOpenSerialWebcamError[] = "Can't open serial webcam."; |
| const char kGetWebcamPTZError[] = "Can't get web camera pan/tilt/zoom."; |
| const char kSetWebcamPTZError[] = "Can't set web camera pan/tilt/zoom."; |
| const char kResetWebcamError[] = "Can't reset web camera."; |
| |
| } // namespace |
| |
| namespace extensions { |
| |
| // static |
| WebcamPrivateAPI* WebcamPrivateAPI::Get(content::BrowserContext* context) { |
| return GetFactoryInstance()->Get(context); |
| } |
| |
| WebcamPrivateAPI::WebcamPrivateAPI(content::BrowserContext* context) |
| : browser_context_(context), |
| weak_ptr_factory_(this) { |
| webcam_resource_manager_.reset( |
| new ApiResourceManager<WebcamResource>(context)); |
| } |
| |
| WebcamPrivateAPI::~WebcamPrivateAPI() { |
| } |
| |
| Webcam* WebcamPrivateAPI::GetWebcam(const std::string& extension_id, |
| const std::string& webcam_id) { |
| WebcamResource* webcam_resource = FindWebcamResource(extension_id, webcam_id); |
| if (webcam_resource) |
| return webcam_resource->GetWebcam(); |
| |
| std::string device_id; |
| GetDeviceId(extension_id, webcam_id, &device_id); |
| V4L2Webcam* v4l2_webcam(new V4L2Webcam(device_id)); |
| if (!v4l2_webcam->Open()) { |
| return nullptr; |
| } |
| |
| webcam_resource_manager_->Add( |
| new WebcamResource(extension_id, v4l2_webcam, webcam_id)); |
| |
| return v4l2_webcam; |
| } |
| |
| bool WebcamPrivateAPI::OpenSerialWebcam( |
| const std::string& extension_id, |
| const std::string& device_path, |
| const base::Callback<void(const std::string&, bool)>& callback) { |
| const std::string& webcam_id = GetWebcamId(extension_id, device_path); |
| WebcamResource* webcam_resource = FindWebcamResource(extension_id, webcam_id); |
| if (webcam_resource) |
| return false; |
| |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| device::mojom::SerialPortPtrInfo port_ptr_info; |
| auto* port_manager = api::SerialPortManager::Get(browser_context_); |
| DCHECK(port_manager); |
| port_manager->GetPort(device_path, mojo::MakeRequest(&port_ptr_info)); |
| |
| ViscaWebcam* visca_webcam = new ViscaWebcam; |
| visca_webcam->Open( |
| extension_id, std::move(port_ptr_info), |
| base::Bind(&WebcamPrivateAPI::OnOpenSerialWebcam, |
| weak_ptr_factory_.GetWeakPtr(), extension_id, device_path, |
| base::WrapRefCounted(visca_webcam), callback)); |
| return true; |
| } |
| |
| bool WebcamPrivateAPI::CloseWebcam(const std::string& extension_id, |
| const std::string& webcam_id) { |
| if (FindWebcamResource(extension_id, webcam_id)) { |
| RemoveWebcamResource(extension_id, webcam_id); |
| return true; |
| } |
| return false; |
| } |
| |
| void WebcamPrivateAPI::OnOpenSerialWebcam( |
| const std::string& extension_id, |
| const std::string& device_path, |
| scoped_refptr<Webcam> webcam, |
| const base::Callback<void(const std::string&, bool)>& callback, |
| bool success) { |
| if (success) { |
| const std::string& webcam_id = GetWebcamId(extension_id, device_path); |
| webcam_resource_manager_->Add( |
| new WebcamResource(extension_id, webcam.get(), webcam_id)); |
| callback.Run(webcam_id, true); |
| } else { |
| callback.Run("", false); |
| } |
| } |
| |
| bool WebcamPrivateAPI::GetDeviceId(const std::string& extension_id, |
| const std::string& webcam_id, |
| std::string* device_id) { |
| url::Origin security_origin = url::Origin::Create( |
| extensions::Extension::GetBaseURLFromExtensionId(extension_id)); |
| |
| return content::GetMediaDeviceIDForHMAC( |
| blink::MEDIA_DEVICE_VIDEO_CAPTURE, |
| browser_context_->GetMediaDeviceIDSalt(), security_origin, webcam_id, |
| device_id); |
| } |
| |
| std::string WebcamPrivateAPI::GetWebcamId(const std::string& extension_id, |
| const std::string& device_id) { |
| url::Origin security_origin = url::Origin::Create( |
| extensions::Extension::GetBaseURLFromExtensionId(extension_id)); |
| |
| return content::GetHMACForMediaDeviceID( |
| browser_context_->GetMediaDeviceIDSalt(), security_origin, device_id); |
| } |
| |
| WebcamResource* WebcamPrivateAPI::FindWebcamResource( |
| const std::string& extension_id, |
| const std::string& webcam_id) const { |
| DCHECK(webcam_resource_manager_); |
| |
| std::unordered_set<int>* connection_ids = |
| webcam_resource_manager_->GetResourceIds(extension_id); |
| if (!connection_ids) |
| return nullptr; |
| |
| for (const auto& connection_id : *connection_ids) { |
| WebcamResource* webcam_resource = |
| webcam_resource_manager_->Get(extension_id, connection_id); |
| if (webcam_resource && webcam_resource->GetWebcamId() == webcam_id) |
| return webcam_resource; |
| } |
| |
| return nullptr; |
| } |
| |
| bool WebcamPrivateAPI::RemoveWebcamResource(const std::string& extension_id, |
| const std::string& webcam_id) { |
| DCHECK(webcam_resource_manager_); |
| |
| std::unordered_set<int>* connection_ids = |
| webcam_resource_manager_->GetResourceIds(extension_id); |
| if (!connection_ids) |
| return false; |
| |
| for (const auto& connection_id : *connection_ids) { |
| WebcamResource* webcam_resource = |
| webcam_resource_manager_->Get(extension_id, connection_id); |
| if (webcam_resource && webcam_resource->GetWebcamId() == webcam_id) { |
| webcam_resource_manager_->Remove(extension_id, connection_id); |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| WebcamPrivateOpenSerialWebcamFunction::WebcamPrivateOpenSerialWebcamFunction() { |
| } |
| |
| WebcamPrivateOpenSerialWebcamFunction:: |
| ~WebcamPrivateOpenSerialWebcamFunction() { |
| } |
| |
| ExtensionFunction::ResponseAction WebcamPrivateOpenSerialWebcamFunction::Run() { |
| std::unique_ptr<webcam_private::OpenSerialWebcam::Params> params( |
| webcam_private::OpenSerialWebcam::Params::Create(*args_)); |
| EXTENSION_FUNCTION_VALIDATE(params.get()); |
| |
| if (WebcamPrivateAPI::Get(browser_context()) |
| ->OpenSerialWebcam( |
| extension_id(), params->path, |
| base::Bind(&WebcamPrivateOpenSerialWebcamFunction::OnOpenWebcam, |
| this))) { |
| // OpenSerialWebcam responds asynchronously. |
| return RespondLater(); |
| } |
| |
| return RespondNow(Error(kPathInUse)); |
| } |
| |
| void WebcamPrivateOpenSerialWebcamFunction::OnOpenWebcam( |
| const std::string& webcam_id, |
| bool success) { |
| if (success) { |
| Respond(OneArgument(std::make_unique<base::Value>(webcam_id))); |
| } else { |
| Respond(Error(kOpenSerialWebcamError)); |
| } |
| } |
| |
| WebcamPrivateCloseWebcamFunction::WebcamPrivateCloseWebcamFunction() { |
| } |
| |
| WebcamPrivateCloseWebcamFunction::~WebcamPrivateCloseWebcamFunction() { |
| } |
| |
| ExtensionFunction::ResponseAction WebcamPrivateCloseWebcamFunction::Run() { |
| std::unique_ptr<webcam_private::CloseWebcam::Params> params( |
| webcam_private::CloseWebcam::Params::Create(*args_)); |
| EXTENSION_FUNCTION_VALIDATE(params.get()); |
| |
| const bool success = WebcamPrivateAPI::Get(browser_context()) |
| ->CloseWebcam(extension_id(), params->webcam_id); |
| return RespondNow(success ? NoArguments() : Error(kUnknownErrorDoNotUse)); |
| } |
| |
| WebcamPrivateSetFunction::WebcamPrivateSetFunction() { |
| } |
| |
| WebcamPrivateSetFunction::~WebcamPrivateSetFunction() { |
| } |
| |
| ExtensionFunction::ResponseAction WebcamPrivateSetFunction::Run() { |
| std::unique_ptr<webcam_private::Set::Params> params( |
| webcam_private::Set::Params::Create(*args_)); |
| EXTENSION_FUNCTION_VALIDATE(params.get()); |
| |
| Webcam* webcam = WebcamPrivateAPI::Get(browser_context()) |
| ->GetWebcam(extension_id(), params->webcam_id); |
| if (!webcam) |
| return RespondNow(Error(kUnknownWebcam)); |
| |
| int pan_speed = 0; |
| int tilt_speed = 0; |
| if (params->config.pan_speed) |
| pan_speed = *(params->config.pan_speed); |
| |
| if (params->config.tilt_speed) |
| tilt_speed = *(params->config.tilt_speed); |
| |
| pending_num_set_webcam_param_requests_ = 0; |
| if (params->config.pan) { |
| ++pending_num_set_webcam_param_requests_; |
| webcam->SetPan( |
| *(params->config.pan), pan_speed, |
| base::Bind(&WebcamPrivateSetFunction::OnSetWebcamParameters, this)); |
| } |
| |
| if (params->config.pan_direction) { |
| Webcam::PanDirection direction = Webcam::PAN_STOP; |
| switch (params->config.pan_direction) { |
| case webcam_private::PAN_DIRECTION_NONE: |
| case webcam_private::PAN_DIRECTION_STOP: |
| direction = Webcam::PAN_STOP; |
| break; |
| |
| case webcam_private::PAN_DIRECTION_RIGHT: |
| direction = Webcam::PAN_RIGHT; |
| break; |
| |
| case webcam_private::PAN_DIRECTION_LEFT: |
| direction = Webcam::PAN_LEFT; |
| break; |
| } |
| ++pending_num_set_webcam_param_requests_; |
| webcam->SetPanDirection( |
| direction, pan_speed, |
| base::Bind(&WebcamPrivateSetFunction::OnSetWebcamParameters, this)); |
| } |
| |
| if (params->config.tilt) { |
| ++pending_num_set_webcam_param_requests_; |
| webcam->SetTilt( |
| *(params->config.tilt), tilt_speed, |
| base::Bind(&WebcamPrivateSetFunction::OnSetWebcamParameters, this)); |
| } |
| |
| if (params->config.tilt_direction) { |
| Webcam::TiltDirection direction = Webcam::TILT_STOP; |
| switch (params->config.tilt_direction) { |
| case webcam_private::TILT_DIRECTION_NONE: |
| case webcam_private::TILT_DIRECTION_STOP: |
| direction = Webcam::TILT_STOP; |
| break; |
| |
| case webcam_private::TILT_DIRECTION_UP: |
| direction = Webcam::TILT_UP; |
| break; |
| |
| case webcam_private::TILT_DIRECTION_DOWN: |
| direction = Webcam::TILT_DOWN; |
| break; |
| } |
| ++pending_num_set_webcam_param_requests_; |
| webcam->SetTiltDirection( |
| direction, tilt_speed, |
| base::Bind(&WebcamPrivateSetFunction::OnSetWebcamParameters, this)); |
| } |
| |
| if (params->config.zoom) { |
| ++pending_num_set_webcam_param_requests_; |
| webcam->SetZoom( |
| *(params->config.zoom), |
| base::Bind(&WebcamPrivateSetFunction::OnSetWebcamParameters, this)); |
| } |
| |
| if (params->config.autofocus_state) { |
| Webcam::AutofocusState state = Webcam::AUTOFOCUS_ON; |
| switch (params->config.autofocus_state) { |
| case webcam_private::AUTOFOCUS_STATE_NONE: |
| case webcam_private::AUTOFOCUS_STATE_OFF: |
| state = Webcam::AUTOFOCUS_OFF; |
| break; |
| |
| case webcam_private::AUTOFOCUS_STATE_ON: |
| state = Webcam::AUTOFOCUS_ON; |
| break; |
| } |
| ++pending_num_set_webcam_param_requests_; |
| webcam->SetAutofocusState( |
| state, |
| base::Bind(&WebcamPrivateSetFunction::OnSetWebcamParameters, this)); |
| } |
| |
| if (params->config.focus) { |
| ++pending_num_set_webcam_param_requests_; |
| webcam->SetFocus( |
| *(params->config.focus), |
| base::Bind(&WebcamPrivateSetFunction::OnSetWebcamParameters, this)); |
| } |
| |
| if (pending_num_set_webcam_param_requests_ == 0) |
| return AlreadyResponded(); |
| |
| return RespondLater(); |
| } |
| |
| void WebcamPrivateSetFunction::OnSetWebcamParameters(bool success) { |
| failed_ |= !success; |
| --pending_num_set_webcam_param_requests_; |
| |
| DCHECK_GE(pending_num_set_webcam_param_requests_, 0); |
| if (pending_num_set_webcam_param_requests_ == 0) |
| Respond(failed_ ? Error(kSetWebcamPTZError) : NoArguments()); |
| } |
| |
| WebcamPrivateGetFunction::WebcamPrivateGetFunction() |
| : min_pan_(0), |
| max_pan_(0), |
| pan_(0), |
| min_tilt_(0), |
| max_tilt_(0), |
| tilt_(0), |
| min_zoom_(0), |
| max_zoom_(0), |
| zoom_(0), |
| min_focus_(0), |
| max_focus_(0), |
| focus_(0), |
| get_pan_(false), |
| get_tilt_(false), |
| get_zoom_(false), |
| get_focus_(false), |
| success_(true) {} |
| |
| WebcamPrivateGetFunction::~WebcamPrivateGetFunction() { |
| } |
| |
| ExtensionFunction::ResponseAction WebcamPrivateGetFunction::Run() { |
| std::unique_ptr<webcam_private::Get::Params> params( |
| webcam_private::Get::Params::Create(*args_)); |
| EXTENSION_FUNCTION_VALIDATE(params.get()); |
| |
| Webcam* webcam = WebcamPrivateAPI::Get(browser_context()) |
| ->GetWebcam(extension_id(), params->webcam_id); |
| if (!webcam) |
| return RespondNow(Error(kUnknownWebcam)); |
| |
| webcam->GetPan(base::Bind(&WebcamPrivateGetFunction::OnGetWebcamParameters, |
| this, INQUIRY_PAN)); |
| webcam->GetTilt(base::Bind(&WebcamPrivateGetFunction::OnGetWebcamParameters, |
| this, INQUIRY_TILT)); |
| webcam->GetZoom(base::Bind(&WebcamPrivateGetFunction::OnGetWebcamParameters, |
| this, INQUIRY_ZOOM)); |
| webcam->GetFocus(base::Bind(&WebcamPrivateGetFunction::OnGetWebcamParameters, |
| this, INQUIRY_FOCUS)); |
| |
| // We might have already responded through OnGetWebcamParameters(). |
| return did_respond() ? AlreadyResponded() : RespondLater(); |
| } |
| |
| void WebcamPrivateGetFunction::OnGetWebcamParameters(InquiryType type, |
| bool success, |
| int value, |
| int min_value, |
| int max_value) { |
| if (!success_) |
| return; |
| success_ = success_ && success; |
| |
| if (!success_) { |
| Respond(Error(kGetWebcamPTZError)); |
| } else { |
| switch (type) { |
| case INQUIRY_PAN: |
| min_pan_ = min_value; |
| max_pan_ = max_value; |
| pan_ = value; |
| get_pan_ = true; |
| break; |
| case INQUIRY_TILT: |
| min_tilt_ = min_value; |
| max_tilt_ = max_value; |
| tilt_ = value; |
| get_tilt_ = true; |
| break; |
| case INQUIRY_ZOOM: |
| min_zoom_ = min_value; |
| max_zoom_ = max_value; |
| zoom_ = value; |
| get_zoom_ = true; |
| break; |
| case INQUIRY_FOCUS: |
| min_focus_ = min_value; |
| max_focus_ = max_value; |
| focus_ = value; |
| get_focus_ = true; |
| break; |
| } |
| if (get_pan_ && get_tilt_ && get_zoom_ && get_focus_) { |
| webcam_private::WebcamCurrentConfiguration result; |
| if (min_pan_ != max_pan_) { |
| result.pan_range = std::make_unique<webcam_private::Range>(); |
| result.pan_range->min = min_pan_; |
| result.pan_range->max = max_pan_; |
| } |
| if (min_tilt_ != max_tilt_) { |
| result.tilt_range = std::make_unique<webcam_private::Range>(); |
| result.tilt_range->min = min_tilt_; |
| result.tilt_range->max = max_tilt_; |
| } |
| if (min_zoom_ != max_zoom_) { |
| result.zoom_range = std::make_unique<webcam_private::Range>(); |
| result.zoom_range->min = min_zoom_; |
| result.zoom_range->max = max_zoom_; |
| } |
| if (min_focus_ != max_focus_) { |
| result.focus_range = std::make_unique<webcam_private::Range>(); |
| result.focus_range->min = min_focus_; |
| result.focus_range->max = max_focus_; |
| } |
| |
| result.pan = pan_; |
| result.tilt = tilt_; |
| result.zoom = zoom_; |
| result.focus = focus_; |
| Respond(OneArgument(result.ToValue())); |
| } |
| } |
| } |
| |
| WebcamPrivateResetFunction::WebcamPrivateResetFunction() { |
| } |
| |
| WebcamPrivateResetFunction::~WebcamPrivateResetFunction() { |
| } |
| |
| ExtensionFunction::ResponseAction WebcamPrivateResetFunction::Run() { |
| std::unique_ptr<webcam_private::Reset::Params> params( |
| webcam_private::Reset::Params::Create(*args_)); |
| EXTENSION_FUNCTION_VALIDATE(params.get()); |
| |
| Webcam* webcam = WebcamPrivateAPI::Get(browser_context()) |
| ->GetWebcam(extension_id(), params->webcam_id); |
| if (!webcam) |
| return RespondNow(Error(kUnknownWebcam)); |
| |
| webcam->Reset(params->config.pan != nullptr, params->config.tilt != nullptr, |
| params->config.zoom != nullptr, |
| base::Bind(&WebcamPrivateResetFunction::OnResetWebcam, this)); |
| |
| // Reset() might have responded already. |
| return did_respond() ? AlreadyResponded() : RespondLater(); |
| } |
| |
| void WebcamPrivateResetFunction::OnResetWebcam(bool success) { |
| Respond(success ? NoArguments() : Error(kResetWebcamError)); |
| } |
| |
| static base::LazyInstance<BrowserContextKeyedAPIFactory<WebcamPrivateAPI>>:: |
| DestructorAtExit g_factory = LAZY_INSTANCE_INITIALIZER; |
| |
| // static |
| BrowserContextKeyedAPIFactory<WebcamPrivateAPI>* |
| WebcamPrivateAPI::GetFactoryInstance() { |
| return g_factory.Pointer(); |
| } |
| |
| template <> |
| void BrowserContextKeyedAPIFactory<WebcamPrivateAPI> |
| ::DeclareFactoryDependencies() { |
| DependsOn(ExtensionsBrowserClient::Get()->GetExtensionSystemFactory()); |
| DependsOn(ProcessManagerFactory::GetInstance()); |
| } |
| |
| } // namespace extensions |