| Index: media/capture/video/chromeos/video_capture_device_arc_chromeos.cc
|
| diff --git a/media/capture/video/chromeos/video_capture_device_arc_chromeos.cc b/media/capture/video/chromeos/video_capture_device_arc_chromeos.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..4c28972223205b8acbe56e141e8dcfca4a7c55bb
|
| --- /dev/null
|
| +++ b/media/capture/video/chromeos/video_capture_device_arc_chromeos.cc
|
| @@ -0,0 +1,334 @@
|
| +// Copyright 2017 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 "media/capture/video/chromeos/video_capture_device_arc_chromeos.h"
|
| +
|
| +#include "media/capture/video/chromeos/camera_device_delegate.h"
|
| +#include "media/capture/video/chromeos/camera_hal_delegate.h"
|
| +#include "ui/display/display.h"
|
| +#include "ui/display/display_observer.h"
|
| +#include "ui/display/screen.h"
|
| +
|
| +namespace media {
|
| +
|
| +using namespace arc::mojom;
|
| +
|
| +// This is a delegate class used to transfer Display change events from the UI
|
| +// thread to the media thread.
|
| +class VideoCaptureDeviceArcChromeOS::ScreenObserverDelegate
|
| + : public display::DisplayObserver,
|
| + public base::RefCountedThreadSafe<ScreenObserverDelegate> {
|
| + public:
|
| + // It is safe to use the raw pointer |capture_device| as
|
| + // VideoCaptureDeviceArcChromeOS owns the ScreenObserverDelegate instance and
|
| + // we make sure the VideoCaptureDeviceArcChromeOS instance outlives the
|
| + // ScreenObserverDelegate instance.
|
| + ScreenObserverDelegate(
|
| + VideoCaptureDeviceArcChromeOS* capture_device,
|
| + scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner)
|
| + : capture_device_(capture_device),
|
| + ui_task_runner_(ui_task_runner),
|
| + capture_task_runner_(base::ThreadTaskRunnerHandle::Get()) {
|
| + ui_task_runner_->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&ScreenObserverDelegate::AddObserverOnUIThread, this));
|
| + }
|
| +
|
| + void RemoveObserver() {
|
| + DCHECK(capture_task_runner_->BelongsToCurrentThread());
|
| + capture_device_ = nullptr;
|
| + ui_task_runner_->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&ScreenObserverDelegate::RemoveObserverOnUIThread, this));
|
| + }
|
| +
|
| + private:
|
| + friend class base::RefCountedThreadSafe<ScreenObserverDelegate>;
|
| +
|
| + ~ScreenObserverDelegate() override { DCHECK(!capture_device_); }
|
| +
|
| + void OnDisplayAdded(const display::Display& /*new_display*/) override {}
|
| + void OnDisplayRemoved(const display::Display& /*old_display*/) override {}
|
| + void OnDisplayMetricsChanged(const display::Display& display,
|
| + uint32_t metrics) override {
|
| + DCHECK(ui_task_runner_->BelongsToCurrentThread());
|
| + if (!(metrics & DISPLAY_METRIC_ROTATION))
|
| + return;
|
| + SendDisplayRotation(display);
|
| + }
|
| +
|
| + void AddObserverOnUIThread() {
|
| + DCHECK(ui_task_runner_->BelongsToCurrentThread());
|
| + display::Screen* screen = display::Screen::GetScreen();
|
| + if (screen) {
|
| + screen->AddObserver(this);
|
| + SendDisplayRotation(screen->GetPrimaryDisplay());
|
| + }
|
| + }
|
| +
|
| + void RemoveObserverOnUIThread() {
|
| + DCHECK(ui_task_runner_->BelongsToCurrentThread());
|
| + display::Screen* screen = display::Screen::GetScreen();
|
| + if (screen)
|
| + screen->RemoveObserver(this);
|
| + }
|
| +
|
| + // Post the screen rotation change from the UI thread to capture thread
|
| + void SendDisplayRotation(const display::Display& display) {
|
| + DCHECK(ui_task_runner_->BelongsToCurrentThread());
|
| + capture_task_runner_->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&ScreenObserverDelegate::SendDisplayRotationOnCaptureThread,
|
| + this, display));
|
| + }
|
| +
|
| + void SendDisplayRotationOnCaptureThread(const display::Display& display) {
|
| + DCHECK(capture_task_runner_->BelongsToCurrentThread());
|
| + if (capture_device_)
|
| + capture_device_->SetDisplayRotation(display);
|
| + }
|
| +
|
| + VideoCaptureDeviceArcChromeOS* capture_device_;
|
| + scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
|
| + scoped_refptr<base::SingleThreadTaskRunner> capture_task_runner_;
|
| + DISALLOW_IMPLICIT_CONSTRUCTORS(ScreenObserverDelegate);
|
| +};
|
| +
|
| +VideoCaptureDeviceArcChromeOS::VideoCaptureDeviceArcChromeOS(
|
| + scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
|
| + const VideoCaptureDeviceDescriptor& device_descriptor,
|
| + scoped_refptr<CameraHalDelegate> camera_hal_delegate)
|
| + : device_descriptor_(device_descriptor),
|
| + camera_hal_delegate_(camera_hal_delegate),
|
| + capture_task_runner_(base::ThreadTaskRunnerHandle::Get()),
|
| + device_thread_("CameraDeviceThread"),
|
| + stopping_(false),
|
| + // TODO(jcliang): Determine |lens_facing_| and |camera_orientation_| from
|
| + // static characteristics. We should probably defer the
|
| + // initialization of |screen_observer_delegate_| until
|
| + // after we get the static characteristics in
|
| + // OnGotCameraInfo.
|
| + screen_observer_delegate_(
|
| + new ScreenObserverDelegate(this, ui_task_runner)),
|
| + lens_facing_(VideoFacingMode::MEDIA_VIDEO_FACING_USER),
|
| + camera_orientation_(0),
|
| + // External cameras have lens_facing as MEDIA_VIDEO_FACING_NONE.
|
| + // We don't want to rotate the frame even if the device rotates.
|
| + rotates_with_device_(lens_facing_ !=
|
| + VideoFacingMode::MEDIA_VIDEO_FACING_NONE) {}
|
| +
|
| +VideoCaptureDeviceArcChromeOS::~VideoCaptureDeviceArcChromeOS() {
|
| + DCHECK(capture_task_runner_->BelongsToCurrentThread());
|
| + DCHECK(!device_thread_.IsRunning());
|
| + screen_observer_delegate_->RemoveObserver();
|
| +}
|
| +
|
| +// static
|
| +VideoPixelFormat VideoCaptureDeviceArcChromeOS::PixFormatHalToChromium(
|
| + HalPixelFormat from) {
|
| + return CameraDeviceDelegate::PixFormatHalToChromium(from);
|
| +}
|
| +
|
| +// static
|
| +uint32_t VideoCaptureDeviceArcChromeOS::PixFormatChromiumToDrm(
|
| + VideoPixelFormat from) {
|
| + return CameraDeviceDelegate::PixFormatChromiumToDrm(from);
|
| +}
|
| +
|
| +// VideoCaptureDevice implementation.
|
| +void VideoCaptureDeviceArcChromeOS::AllocateAndStart(
|
| + const VideoCaptureParams& params,
|
| + std::unique_ptr<Client> client) {
|
| + DCHECK(capture_task_runner_->BelongsToCurrentThread());
|
| + // AllocateAndStart, StopAndDeAllocate, and creation / deletion of
|
| + // |camera_device_delegate_| all happen on |capture_task_runner_|. When
|
| + // StopAndDeAllocate is called, we either 1) reset |camera_device_delegate_|
|
| + // immediately if it's set, or 2) drop the on-going open process in
|
| + // CreateDeviceOnCaptureThread. In case 1, it safe; in case 2, the
|
| + // |camera_device_delegate_| is not set when StopAndDeAllocate is called, and
|
| + // wouldn't be set in CreateDeviceOnCaptureThread, either. So back-to-back
|
| + // AllocateAndStart, StopAndDeAllocate, and AllocateAndStart calls wouldn't
|
| + // hit this DCHECK.
|
| + DCHECK(!camera_device_delegate_);
|
| + int32_t camera_id = std::stoi(device_descriptor_.device_id);
|
| + camera_hal_delegate_->GetCameraInfo(
|
| + camera_id,
|
| + base::Bind(&VideoCaptureDeviceArcChromeOS::OnGotCameraInfo,
|
| + base::Unretained(this), params, base::Passed(&client)));
|
| +}
|
| +
|
| +void VideoCaptureDeviceArcChromeOS::StopAndDeAllocate() {
|
| + DCHECK(capture_task_runner_->BelongsToCurrentThread());
|
| + stopping_ = true;
|
| + // StopAndDeAllocate is called only after AllocateAndStart. We have two cases
|
| + // here: |camera_device_delegate_| is either created, or being created. If
|
| + // |camera_device_delegate_| is already created then we simply close the
|
| + // camera device here; otherwise we defer to CreateDeviceOnCaptureThread to
|
| + // close the camera device right after camera HAL has opened the camera
|
| + // device.
|
| + if (camera_device_delegate_) {
|
| + base::WaitableEvent closed(base::WaitableEvent::ResetPolicy::MANUAL,
|
| + base::WaitableEvent::InitialState::NOT_SIGNALED);
|
| + device_thread_.task_runner()->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&CameraDeviceDelegate::StopAndDeAllocate,
|
| + camera_device_delegate_, base::Unretained(&closed)));
|
| + closed.Wait();
|
| + camera_device_delegate_ = nullptr;
|
| + device_thread_.Stop();
|
| + stopping_ = false;
|
| + }
|
| +}
|
| +
|
| +void VideoCaptureDeviceArcChromeOS::TakePhoto(TakePhotoCallback callback) {
|
| + DCHECK(capture_task_runner_->BelongsToCurrentThread());
|
| + DCHECK(camera_device_delegate_);
|
| + device_thread_.task_runner()->PostTask(
|
| + FROM_HERE, base::Bind(&CameraDeviceDelegate::TakePhoto,
|
| + camera_device_delegate_, base::Passed(&callback)));
|
| +}
|
| +
|
| +void VideoCaptureDeviceArcChromeOS::GetPhotoCapabilities(
|
| + GetPhotoCapabilitiesCallback callback) {
|
| + DCHECK(capture_task_runner_->BelongsToCurrentThread());
|
| + device_thread_.task_runner()->PostTask(
|
| + FROM_HERE, base::Bind(&CameraDeviceDelegate::GetPhotoCapabilities,
|
| + camera_device_delegate_, base::Passed(&callback)));
|
| +}
|
| +
|
| +void VideoCaptureDeviceArcChromeOS::OnGotCameraInfo(
|
| + const VideoCaptureParams& params,
|
| + std::unique_ptr<Client> client,
|
| + int32_t result,
|
| + CameraInfoPtr camera_info) {
|
| + // This method runs on |module_task_runner_| of |camera_hal_delegate_|.
|
| + if (result) {
|
| + client->OnError(FROM_HERE, "Failed to get camera info");
|
| + return;
|
| + }
|
| + int32_t camera_id = std::stoi(device_descriptor_.device_id);
|
| + camera_hal_delegate_->OpenDevice(
|
| + camera_id,
|
| + base::Bind(&VideoCaptureDeviceArcChromeOS::OnOpenedDevice,
|
| + base::Unretained(this), params, base::Passed(&client),
|
| + base::Passed(&camera_info->static_camera_characteristics)));
|
| +}
|
| +
|
| +void VideoCaptureDeviceArcChromeOS::OnOpenedDevice(
|
| + const VideoCaptureParams& params,
|
| + std::unique_ptr<Client> client,
|
| + CameraMetadataPtr static_metadata,
|
| + int32_t result,
|
| + Camera3DeviceOpsPtr device_ops) {
|
| + // This method runs on |module_task_runner_| of |camera_hal_delegate_|.
|
| + if (result) {
|
| + client->OnError(FROM_HERE, "Failed to open camera device");
|
| + return;
|
| + }
|
| + mojo::InterfacePtrInfo<Camera3DeviceOps> device_ops_info =
|
| + device_ops.PassInterface();
|
| + capture_task_runner_->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&VideoCaptureDeviceArcChromeOS::CreateDeviceOnCaptureThread,
|
| + base::Unretained(this), params, base::Passed(&client),
|
| + base::Passed(&static_metadata), result,
|
| + base::Passed(&device_ops_info)));
|
| +}
|
| +
|
| +void VideoCaptureDeviceArcChromeOS::CreateDeviceOnCaptureThread(
|
| + const VideoCaptureParams& params,
|
| + std::unique_ptr<Client> client,
|
| + CameraMetadataPtr static_metadata,
|
| + int32_t result,
|
| + mojo::InterfacePtrInfo<Camera3DeviceOps> device_ops_info) {
|
| + DCHECK(capture_task_runner_->BelongsToCurrentThread());
|
| +
|
| + if (stopping_) {
|
| + // A StopAndDeAllocate call was made.
|
| + Camera3DeviceOpsPtr device_ops;
|
| + device_ops.Bind(std::move(device_ops_info));
|
| + base::Callback<void(int32_t)> dont_care = base::Bind([](int32_t result) {});
|
| + // We don't need to worry about the return value of the Close mojo call here
|
| + // since the Camera3DeviceOps proxy will be terminated immediately. The next
|
| + // AllocateAndStart will create a new Camera3DeviceOps proxy.
|
| + device_ops->Close(dont_care);
|
| + stopping_ = false;
|
| + return;
|
| + }
|
| + device_thread_.Start();
|
| + camera_device_delegate_ = new CameraDeviceDelegate(
|
| + device_descriptor_, std::move(static_metadata),
|
| + std::move(device_ops_info), device_thread_.task_runner());
|
| + device_thread_.task_runner()->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&CameraDeviceDelegate::AllocateAndStart,
|
| + camera_device_delegate_, params, base::Passed(&client)));
|
| +}
|
| +
|
| +void VideoCaptureDeviceArcChromeOS::SetPhotoOptions(
|
| + mojom::PhotoSettingsPtr settings,
|
| + SetPhotoOptionsCallback callback) {
|
| + DCHECK(capture_task_runner_->BelongsToCurrentThread());
|
| + device_thread_.task_runner()->PostTask(
|
| + FROM_HERE, base::Bind(&CameraDeviceDelegate::SetPhotoOptions,
|
| + camera_device_delegate_, base::Passed(&settings),
|
| + base::Passed(&callback)));
|
| +}
|
| +
|
| +void VideoCaptureDeviceArcChromeOS::SetRotation(int rotation) {
|
| + DCHECK(capture_task_runner_->BelongsToCurrentThread());
|
| + if (!rotates_with_device_) {
|
| + rotation = 0;
|
| + } else if (lens_facing_ == VideoFacingMode::MEDIA_VIDEO_FACING_ENVIRONMENT) {
|
| + // Original frame when |rotation| = 0
|
| + // -----------------------
|
| + // | * |
|
| + // | * * |
|
| + // | * * |
|
| + // | ******* |
|
| + // | * * |
|
| + // | * * |
|
| + // -----------------------
|
| + //
|
| + // |rotation| = 90, this is what back camera sees
|
| + // -----------------------
|
| + // | ******** |
|
| + // | * **** |
|
| + // | * *** |
|
| + // | * *** |
|
| + // | * **** |
|
| + // | ******** |
|
| + // -----------------------
|
| + //
|
| + // |rotation| = 90, this is what front camera sees
|
| + // -----------------------
|
| + // | ******** |
|
| + // | **** * |
|
| + // | *** * |
|
| + // | *** * |
|
| + // | **** * |
|
| + // | ******** |
|
| + // -----------------------
|
| + //
|
| + // Therefore, for back camera, we need to rotate (360 - |rotation|).
|
| + rotation = (360 - rotation) % 360;
|
| + }
|
| + // Take into account camera orientation w.r.t. the display. External cameras
|
| + // would have camera_orientation_ as 0.
|
| + rotation = (rotation + camera_orientation_) % 360;
|
| + if (device_thread_.IsRunning()) {
|
| + device_thread_.task_runner()->PostTask(
|
| + FROM_HERE, base::Bind(&CameraDeviceDelegate::SetRotation,
|
| + camera_device_delegate_, rotation));
|
| + }
|
| +}
|
| +
|
| +void VideoCaptureDeviceArcChromeOS::SetDisplayRotation(
|
| + const display::Display& display) {
|
| + DCHECK(capture_task_runner_->BelongsToCurrentThread());
|
| + if (display.IsInternal())
|
| + SetRotation(display.rotation() * 90);
|
| +}
|
| +
|
| +} // namespace media
|
|
|