/* * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the License); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an AS IS BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; using System.Threading; using Native = Interop.Camera; namespace Tizen.Multimedia { static internal class CameraLog { internal const string Tag = "Tizen.Multimedia.Camera"; internal const string Enter = "[Enter]"; internal const string Leave = "[Leave]"; } /// /// This camera class provides methods to capture photos and supports setting up notifications /// for state changes of capturing, previewing, focusing, and informing about the resolution and the binary format, /// and functions for picture manipulations like sepia, negative, and many more. /// It also notifies you when a significant picture parameter changes, (For example, focus). /// /// 3 /// http://tizen.org/feature/camera public partial class Camera : IDisposable, IDisplayable { private IntPtr _handle = IntPtr.Zero; private bool _disposed = false; private CameraState _state = CameraState.None; PinnedPreviewBuffer _previewBuffer; /// /// Initializes a new instance of the class. /// /// The camera device to access. /// Invalid CameraDevice type. /// In case of any invalid operations. /// The camera feature is not supported. /// 3 /// http://tizen.org/feature/camera public Camera(CameraDevice device) { ValidationUtil.ValidateEnum(typeof(CameraDevice), device, nameof(device)); CreateCameraDevice(device); Initialize(); } /// /// Initializes a new instance of the class. /// /// CameraDevice and Type will be selected internally by CameraDeviceManager. /// In case of any invalid operations. /// The camera feature is not supported. /// 9 /// http://tizen.org/feature/camera [EditorBrowsable(EditorBrowsableState.Never)] public Camera() : this(CameraDevice.Default) { } private void Initialize() { Capabilities = new CameraCapabilities(this); Settings = new CameraSettings(this); DisplaySettings = new CameraDisplaySettings(this); RegisterCallbacks(); SetState(CameraState.Created); } private void CreateCameraDevice(CameraDevice device) { CameraDeviceType cameraDeviceType = CameraDeviceType.BuiltIn; CameraDevice cameraDevice = device; if (CameraDeviceManager.IsSupported || device == CameraDevice.Default) { var deviceInfo = GetDeviceInformation(); if (!deviceInfo.Any()) { throw new InvalidOperationException("CDM is supported but, there's no available camera device."); } cameraDeviceType = deviceInfo.First().Type; cameraDevice = deviceInfo.First().Device; Log.Debug(CameraLog.Tag, $"Type:[{cameraDeviceType}], Device:[{cameraDevice}]"); } CreateNativeCameraDevice(cameraDeviceType, cameraDevice); } private IEnumerable GetDeviceInformation() { using (var cameraDeviceManager = new CameraDeviceManager()) { return cameraDeviceManager.GetDeviceInformation(); } } private void CreateNativeCameraDevice(CameraDeviceType type, CameraDevice device) { if (type == CameraDeviceType.BuiltIn || type == CameraDeviceType.Usb) { Native.Create(device, out _handle). ThrowIfFailed($"Failed to create {type} camera"); } else { Native.CreateNetworkCamera(device, out _handle). ThrowIfFailed($"Failed to create {type} camera"); } } /// /// Finalizes an instance of the Camera class. /// ~Camera() { Dispose(false); } /// /// Gets the native handle of the camera. /// /// 3 /// http://tizen.org/feature/camera public IntPtr Handle => GetHandle(); internal IntPtr GetHandle() { ValidateNotDisposed(); return _handle; } #region Dispose support /// /// Releases the unmanaged resources used by the camera. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. /// 3 protected virtual void Dispose(bool disposing) { if (!_disposed) { if (disposing) { // to be used if there are any other disposable objects _previewBuffer?.Dispose(); } if (_handle != IntPtr.Zero) { Native.Destroy(_handle); _handle = IntPtr.Zero; } _disposed = true; } } /// /// Releases all resources used by the camera. /// /// 3 /// http://tizen.org/feature/camera public void Dispose() { ReplaceDisplay(null); Dispose(true); GC.SuppressFinalize(this); } internal void ValidateNotDisposed() { if (_disposed) { Log.Error(CameraLog.Tag, "Camera handle is disposed."); throw new ObjectDisposedException(nameof(Camera)); } } #endregion Dispose support internal void ValidateState(params CameraState[] required) { ValidateNotDisposed(); Debug.Assert(required.Length > 0); var curState = _state; if (!required.Contains(curState)) { throw new InvalidOperationException($"The camera is not in a valid state. " + $"Current State : { curState }, Valid State : { string.Join(", ", required) }."); } } internal void SetState(CameraState state) { _state = state; } /// /// Changes the camera device. /// /// The hardware camera to access. /// 3 /// http://tizen.org/feature/camera /// /// If display reuse is set using /// before stopping the preview, the display will be reused and last frame on the display /// can be kept even though camera device is changed. /// The camera must be in the . /// /// In case of invalid parameters. /// In case of any invalid operations. /// In case of the ChangeDevice feature is not supported. /// The camera already has been disposed of. public void ChangeDevice(CameraDevice device) { ValidateState(CameraState.Created); ValidationUtil.ValidateEnum(typeof(CameraDevice), device, nameof(device)); Native.ChangeDevice(_handle, device).ThrowIfFailed("Failed to change the camera device"); } /// /// Gets the device state. /// /// The device to get the state. /// Returns the state of the camera device. /// 4 /// http://tizen.org/feature/camera /// In case of invalid parameters. /// In case of any invalid operations. /// In case of this feature is not supported. public static CameraDeviceState GetDeviceState(CameraDevice device) { ValidationUtil.ValidateEnum(typeof(CameraDevice), device, nameof(device)); Native.GetDeviceState(device, out var val).ThrowIfFailed("Failed to get the camera device state."); return val; } /// /// Gets the flash state. /// /// The device to get the state. /// Returns the flash state of the camera device. /// 3 /// http://tizen.org/feature/camera /// In case of invalid parameters. /// In case of any invalid operations. /// In case of this feature is not supported. public static CameraFlashState GetFlashState(CameraDevice device) { ValidationUtil.ValidateEnum(typeof(CameraDevice), device, nameof(device)); Native.GetFlashState(device, out var val).ThrowIfFailed("Failed to get camera flash state"); return val; } /// /// Starts capturing and drawing preview frames on the screen. /// The display property must be set using before using this method. /// If needed set fps , preview resolution /// , or preview format /// before using this method. /// The camera must be in the or the state. /// /// 3 /// http://tizen.org/privilege/camera /// http://tizen.org/feature/camera /// In case of any invalid operations. /// In case of this feature is not supported. /// The camera already has been disposed of. /// In case of access to the resources cannot be granted. public void StartPreview() { ValidateState(CameraState.Created, CameraState.Captured); Native.StartPreview(_handle).ThrowIfFailed("Failed to start the camera preview."); // Update by StateChangedCallback can be delayed for dozens of milliseconds. SetState(CameraState.Preview); } /// /// Stops capturing and drawing preview frames on the screen. /// The camera must be in the state. /// /// 3 /// http://tizen.org/privilege/camera /// http://tizen.org/feature/camera /// In case of any invalid operations. /// In case of this feature is not supported. /// The camera already has been disposed of. /// In case of access to the resources cannot be granted. public void StopPreview() { ValidateState(CameraState.Preview); Native.StopPreview(_handle).ThrowIfFailed("Failed to stop the camera preview."); SetState(CameraState.Created); } /// /// Starts capturing of still images. /// EventHandler must be set for capturing using /// and for completed using before calling this method. /// The camera must be in the state. /// /// 3 /// http://tizen.org/privilege/camera /// http://tizen.org/feature/camera /// /// This function causes the transition of the camera state from capturing to captured /// automatically and the corresponding EventHandlers will be invoked. /// The preview should be restarted by calling the method after capture is completed. /// /// In case of any invalid operations. /// In case of this feature is not supported. /// The camera already has been disposed of. /// In case of access to the resources cannot be granted. public void StartCapture() { ValidateState(CameraState.Preview); Native.StartCapture(_handle, _capturingCallback, _captureCompletedCallback). ThrowIfFailed("Failed to start the camera capture."); SetState(CameraState.Capturing); } /// /// Starts continuously capturing still images. /// EventHandler must be set for capturing using /// and for completed using before calling this method. /// The camera must be in the state. /// /// The number of still images. /// The interval of the capture(milliseconds). /// The cancellation token to cancel capturing. /// /// 3 /// http://tizen.org/privilege/camera /// http://tizen.org/feature/camera /// /// If this is not supported, zero shutter lag occurs. The capture resolution could be /// changed to the preview resolution. This function causes the transition of the camera state /// from capturing to captured automatically and the corresponding Eventhandlers will be invoked. /// Each captured image will be delivered through Eventhandler set using the event. /// The preview should be restarted by calling the method after capture is completed. /// /// In case of invalid parameters. /// In case of any invalid operations. /// In case of this feature is not supported. /// The camera already has been disposed of. /// In case of access to the resources cannot be granted. public void StartCapture(int count, int interval, CancellationToken cancellationToken) { ValidateState(CameraState.Preview); if (count < 2) { throw new ArgumentOutOfRangeException(nameof(count), count, $"{nameof(count)} should be greater than one."); } if (interval < 0) { throw new ArgumentOutOfRangeException(nameof(interval), interval, $"{nameof(interval)} should be greater than or equal to zero."); } //Handle CancellationToken if (cancellationToken != CancellationToken.None) { cancellationToken.Register(() => { Native.StopContinuousCapture(_handle).ThrowIfFailed("Failed to cancel the continuous capture"); SetState(CameraState.Captured); }); } Native.StartContinuousCapture(_handle, count, interval, _capturingCallback, _captureCompletedCallback). ThrowIfFailed("Failed to start the continuous capture."); SetState(CameraState.Capturing); } /// /// Starts camera auto-focusing, asynchronously. /// The camera must be in the or the state. /// /// Continuous auto focus. /// 3 /// http://tizen.org/privilege/camera /// http://tizen.org/feature/camera /// /// If continuous status is true, the camera continuously tries to focus. /// /// In case of invalid parameters. /// In case of any invalid operations. /// In case of this feature is not supported. /// The camera already has been disposed of. /// In case of access to the resources cannot be granted. public void StartFocusing(bool continuous) { ValidateState(CameraState.Preview, CameraState.Captured); Native.StartFocusing(_handle, continuous).ThrowIfFailed("Failed to cancel the camera focus."); } /// /// Stops camera auto focusing. /// The camera must be in the or the state. /// /// 3 /// http://tizen.org/privilege/camera /// http://tizen.org/feature/camera /// In case of any invalid operations. /// In case of this feature is not supported. /// The camera already has been disposed of. /// In case of access to the resources cannot be granted. public void StopFocusing() { ValidateState(CameraState.Preview, CameraState.Captured); Native.CancelFocusing(_handle).ThrowIfFailed("Failed to cancel the camera focus."); } /// /// Starts face detection. /// The camera must be in the state. /// /// 3 /// http://tizen.org/privilege/camera /// http://tizen.org/feature/camera /// /// This should be called after is started. /// The Eventhandler set using is invoked when the face is detected in the preview frame. /// Internally, it starts continuously focus and focusing on the detected face. /// /// In case of any invalid operations. /// In case of this feature is not supported. /// The camera already has been disposed of. /// In case of access to the resources cannot be granted. public void StartFaceDetection() { ValidateState(CameraState.Preview); _faceDetectedCallback = (IntPtr faces, int count, IntPtr userData) => { var result = new List(); IntPtr current = faces; for (int i = 0; i < count; i++) { result.Add(new FaceDetectionData(current)); current = IntPtr.Add(current, Marshal.SizeOf()); } FaceDetected?.Invoke(this, new FaceDetectedEventArgs(result)); }; Native.StartFaceDetection(_handle, _faceDetectedCallback). ThrowIfFailed("Failed to start face detection"); } /// /// Stops face detection. /// /// 3 /// http://tizen.org/privilege/camera /// http://tizen.org/feature/camera /// In case of any invalid operations. /// In case of this feature is not supported. /// The camera already has been disposed of. /// In case of access to the resources cannot be granted. public void StopFaceDetection() { ValidateNotDisposed(); if (_faceDetectedCallback == null) { throw new InvalidOperationException("The face detection is not started."); } Native.StopFaceDetection(_handle).ThrowIfFailed("Failed to stop the face detection."); _faceDetectedCallback = null; } } }