[Camera] Add new internal API - extra preview event (#3011)
[platform/core/csapi/tizenfx.git] / src / Tizen.Multimedia.Camera / Camera / Camera.cs
1 /*
2  * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the License);
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an AS IS BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 using System;
18 using System.Collections.Generic;
19 using System.Diagnostics;
20 using System.Linq;
21 using System.Runtime.InteropServices;
22 using System.Threading;
23 using Native = Interop.Camera;
24
25 namespace Tizen.Multimedia
26 {
27     static internal class CameraLog
28     {
29         internal const string Tag = "Tizen.Multimedia.Camera";
30         internal const string Enter = "[Enter]";
31         internal const string Leave = "[Leave]";
32     }
33
34     /// <summary>
35     /// This camera class provides methods to capture photos and supports setting up notifications
36     /// for state changes of capturing, previewing, focusing, and informing about the resolution and the binary format,
37     /// and functions for picture manipulations like sepia, negative, and many more.
38     /// It also notifies you when a significant picture parameter changes, (For example, focus).
39     /// </summary>
40     /// <since_tizen> 3 </since_tizen>
41     /// <feature> http://tizen.org/feature/camera </feature>
42     public partial class Camera : IDisposable, IDisplayable<CameraError>
43     {
44         private IntPtr _handle = IntPtr.Zero;
45         private bool _disposed = false;
46         private CameraState _state = CameraState.None;
47
48         /// <summary>
49         /// Initializes a new instance of the <see cref="Camera"/> class.
50         /// </summary>
51         /// <param name="device">The camera device to access.</param>
52         /// <exception cref="ArgumentException">Invalid CameraDevice type.</exception>
53         /// <exception cref="NotSupportedException">The camera feature is not supported.</exception>
54         /// <since_tizen> 3 </since_tizen>
55         /// <feature> http://tizen.org/feature/camera </feature>
56         public Camera(CameraDevice device)
57         {
58             Native.Create(device, out _handle).ThrowIfFailed("Failed to create camera instance");
59
60             Capabilities = new CameraCapabilities(this);
61             Settings = new CameraSettings(this);
62             DisplaySettings = new CameraDisplaySettings(this);
63
64             RegisterCallbacks();
65
66             SetState(CameraState.Created);
67         }
68
69         /// <summary>
70         /// Finalizes an instance of the Camera class.
71         /// </summary>
72         ~Camera()
73         {
74             Dispose(false);
75         }
76
77         /// <summary>
78         /// Gets the native handle of the camera.
79         /// </summary>
80         /// <since_tizen> 3 </since_tizen>
81         /// <feature> http://tizen.org/feature/camera </feature>
82         public IntPtr Handle => GetHandle();
83
84         internal IntPtr GetHandle()
85         {
86             ValidateNotDisposed();
87             return _handle;
88         }
89
90         #region Dispose support
91         /// <summary>
92         /// Releases the unmanaged resources used by the camera.
93         /// </summary>
94         /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
95         /// <since_tizen> 3 </since_tizen>
96         protected virtual void Dispose(bool disposing)
97         {
98             if (!_disposed)
99             {
100                 if (disposing)
101                 {
102                     // to be used if there are any other disposable objects
103                 }
104
105                 if (_handle != IntPtr.Zero)
106                 {
107                     Native.Destroy(_handle);
108                     _handle = IntPtr.Zero;
109                 }
110
111                 _disposed = true;
112             }
113         }
114
115         /// <summary>
116         /// Releases all resources used by the camera.
117         /// </summary>
118         /// <since_tizen> 3 </since_tizen>
119         /// <feature> http://tizen.org/feature/camera </feature>
120         public void Dispose()
121         {
122             ReplaceDisplay(null);
123             Dispose(true);
124             GC.SuppressFinalize(this);
125         }
126
127         internal void ValidateNotDisposed()
128         {
129             if (_disposed)
130             {
131                 Log.Error(CameraLog.Tag, "Camera handle is disposed.");
132                 throw new ObjectDisposedException(nameof(Camera));
133             }
134         }
135         #endregion Dispose support
136
137         #region Check camera state
138         internal void ValidateState(params CameraState[] required)
139         {
140             ValidateNotDisposed();
141
142             Debug.Assert(required.Length > 0);
143
144             var curState = _state;
145             if (!required.Contains(curState))
146             {
147                 throw new InvalidOperationException($"The camera is not in a valid state. " +
148                     $"Current State : { curState }, Valid State : { string.Join(", ", required) }.");
149             }
150         }
151
152         internal void SetState(CameraState state)
153         {
154             _state = state;
155         }
156         #endregion Check camera state
157
158
159
160         #region Methods
161         /// <summary>
162         /// Changes the camera device.
163         /// </summary>
164         /// <param name="device">The hardware camera to access.</param>
165         /// <since_tizen> 3 </since_tizen>
166         /// <feature> http://tizen.org/feature/camera </feature>
167         /// <remarks>
168         /// If display reuse is set using <see cref="DisplayReuseHint"/>
169         /// before stopping the preview, the display will be reused and last frame on the display
170         /// can be kept even though camera device is changed.
171         /// The camera must be in the <see cref="CameraState.Created"/>.
172         /// </remarks>
173         /// <exception cref="ArgumentException">In case of invalid parameters.</exception>
174         /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
175         /// <exception cref="NotSupportedException">In case of the ChangeDevice feature is not supported.</exception>
176         /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
177         public void ChangeDevice(CameraDevice device)
178         {
179             ValidateState(CameraState.Created);
180             ValidationUtil.ValidateEnum(typeof(CameraDevice), device, nameof(device));
181
182             Native.ChangeDevice(_handle, device).ThrowIfFailed("Failed to change the camera device");
183         }
184
185         /// <summary>
186         /// Gets the device state.
187         /// </summary>
188         /// <param name="device">The device to get the state.</param>
189         /// <returns>Returns the state of the camera device.</returns>
190         /// <since_tizen> 4 </since_tizen>
191         /// <feature> http://tizen.org/feature/camera </feature>
192         /// <exception cref="ArgumentException">In case of invalid parameters.</exception>
193         /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
194         /// <exception cref="NotSupportedException">In case of this feature is not supported.</exception>
195         public static CameraDeviceState GetDeviceState(CameraDevice device)
196         {
197             ValidationUtil.ValidateEnum(typeof(CameraDevice), device, nameof(device));
198
199             Native.GetDeviceState(device, out var val).ThrowIfFailed("Failed to get the camera device state.");
200
201             return val;
202         }
203
204         /// <summary>
205         /// Gets the flash state.
206         /// </summary>
207         /// <param name="device">The device to get the state.</param>
208         /// <returns>Returns the flash state of the camera device.</returns>
209         /// <since_tizen> 3 </since_tizen>
210         /// <feature> http://tizen.org/feature/camera </feature>
211         /// <exception cref="ArgumentException">In case of invalid parameters.</exception>
212         /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
213         /// <exception cref="NotSupportedException">In case of this feature is not supported.</exception>
214         public static CameraFlashState GetFlashState(CameraDevice device)
215         {
216             ValidationUtil.ValidateEnum(typeof(CameraDevice), device, nameof(device));
217
218             Native.GetFlashState(device, out var val).ThrowIfFailed("Failed to get camera flash state");
219
220             return val;
221         }
222
223         /// <summary>
224         /// Starts capturing and drawing preview frames on the screen.
225         /// The display property must be set using <see cref="Display"/> before using this method.
226         /// If needed set fps <see cref="CameraSettings.PreviewFps"/>, preview resolution
227         /// <see cref="CameraSettings.PreviewResolution"/>, or preview format <see cref="CameraSettings.PreviewPixelFormat"/>
228         /// before using this method.
229         /// The camera must be in the <see cref="CameraState.Created"/> or the <see cref="CameraState.Captured"/> state.
230         /// </summary>
231         /// <since_tizen> 3 </since_tizen>
232         /// <privilege> http://tizen.org/privilege/camera </privilege>
233         /// <feature> http://tizen.org/feature/camera </feature>
234         /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
235         /// <exception cref="NotSupportedException">In case of this feature is not supported.</exception>
236         /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
237         /// <exception cref="UnauthorizedAccessException">In case of access to the resources cannot be granted.</exception>
238         public void StartPreview()
239         {
240             ValidateState(CameraState.Created, CameraState.Captured);
241
242             Native.StartPreview(_handle).ThrowIfFailed("Failed to start the camera preview.");
243
244             // Update by StateChangedCallback can be delayed for dozens of milliseconds.
245             SetState(CameraState.Preview);
246         }
247
248         /// <summary>
249         /// Stops capturing and drawing preview frames on the screen.
250         /// The camera must be in the <see cref="CameraState.Preview"/> state.
251         /// </summary>
252         /// <since_tizen> 3 </since_tizen>
253         /// <privilege> http://tizen.org/privilege/camera </privilege>
254         /// <feature> http://tizen.org/feature/camera </feature>
255         /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
256         /// <exception cref="NotSupportedException">In case of this feature is not supported.</exception>
257         /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
258         /// <exception cref="UnauthorizedAccessException">In case of access to the resources cannot be granted.</exception>
259         public void StopPreview()
260         {
261             ValidateState(CameraState.Preview);
262
263             Native.StopPreview(_handle).ThrowIfFailed("Failed to stop the camera preview.");
264
265             SetState(CameraState.Created);
266         }
267
268         /// <summary>
269         /// Starts capturing of still images.
270         /// EventHandler must be set for capturing using <see cref="Capturing"/>
271         /// and for completed using <see cref="CaptureCompleted"/> before calling this method.
272         /// The camera must be in the <see cref="CameraState.Preview"/> state.
273         /// </summary>
274         /// <since_tizen> 3 </since_tizen>
275         /// <privilege> http://tizen.org/privilege/camera </privilege>
276         /// <feature> http://tizen.org/feature/camera </feature>
277         /// <remarks>
278         /// This function causes the transition of the camera state from capturing to captured
279         /// automatically and the corresponding EventHandlers will be invoked.
280         /// The preview should be restarted by calling the <see cref="StartPreview"/> method after capture is completed.
281         /// </remarks>
282         /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
283         /// <exception cref="NotSupportedException">In case of this feature is not supported.</exception>
284         /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
285         /// <exception cref="UnauthorizedAccessException">In case of access to the resources cannot be granted.</exception>
286         public void StartCapture()
287         {
288             ValidateState(CameraState.Preview);
289
290             Native.StartCapture(_handle, _capturingCallback, _captureCompletedCallback).
291                 ThrowIfFailed("Failed to start the camera capture.");
292
293             SetState(CameraState.Capturing);
294         }
295
296         /// <summary>
297         /// Starts continuously capturing still images.
298         /// EventHandler must be set for capturing using <see cref="Capturing"/>
299         /// and for completed using <see cref="CaptureCompleted"/> before calling this method.
300         /// The camera must be in the <see cref="CameraState.Preview"/> state.
301         /// </summary>
302         /// <param name="count">The number of still images.</param>
303         /// <param name="interval">The interval of the capture(milliseconds).</param>
304         /// <param name="cancellationToken">The cancellation token to cancel capturing.</param>
305         /// <seealso cref="CancellationToken"/>
306         /// <since_tizen> 3 </since_tizen>
307         /// <privilege> http://tizen.org/privilege/camera </privilege>
308         /// <feature> http://tizen.org/feature/camera </feature>
309         /// <remarks>
310         /// If this is not supported, zero shutter lag occurs. The capture resolution could be
311         /// changed to the preview resolution. This function causes the transition of the camera state
312         /// from capturing to captured automatically and the corresponding Eventhandlers will be invoked.
313         /// Each captured image will be delivered through Eventhandler set using the <see cref="Capturing"/> event.
314         /// The preview should be restarted by calling the <see cref="StartPreview"/> method after capture is completed.
315         /// </remarks>
316         /// <exception cref="ArgumentOutOfRangeException">In case of invalid parameters.</exception>
317         /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
318         /// <exception cref="NotSupportedException">In case of this feature is not supported.</exception>
319         /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
320         /// <exception cref="UnauthorizedAccessException">In case of access to the resources cannot be granted.</exception>
321         public void StartCapture(int count, int interval, CancellationToken cancellationToken)
322         {
323             ValidateState(CameraState.Preview);
324
325             if (count < 2)
326             {
327                 throw new ArgumentOutOfRangeException(nameof(count), count, $"{nameof(count)} should be greater than one.");
328             }
329
330             if (interval < 0)
331             {
332                 throw new ArgumentOutOfRangeException(nameof(interval), interval, $"{nameof(interval)} should be greater than or equal to zero.");
333             }
334
335             //Handle CancellationToken
336             if (cancellationToken != CancellationToken.None)
337             {
338                 cancellationToken.Register(() =>
339                 {
340                     Native.StopContinuousCapture(_handle).ThrowIfFailed("Failed to cancel the continuous capture");
341                     SetState(CameraState.Captured);
342                 });
343             }
344
345             Native.StartContinuousCapture(_handle, count, interval, _capturingCallback, _captureCompletedCallback).
346                 ThrowIfFailed("Failed to start the continuous capture.");
347
348             SetState(CameraState.Capturing);
349         }
350
351         /// <summary>
352         /// Starts camera auto-focusing, asynchronously.
353         /// The camera must be in the <see cref="CameraState.Preview"/> or the <see cref="CameraState.Captured"/> state.
354         /// </summary>
355         /// <param name="continuous">Continuous auto focus.</param>
356         /// <since_tizen> 3 </since_tizen>
357         /// <privilege> http://tizen.org/privilege/camera </privilege>
358         /// <feature> http://tizen.org/feature/camera </feature>
359         /// <remarks>
360         /// If continuous status is true, the camera continuously tries to focus.
361         /// </remarks>
362         /// <exception cref="ArgumentException">In case of invalid parameters.</exception>
363         /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
364         /// <exception cref="NotSupportedException">In case of this feature is not supported.</exception>
365         /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
366         /// <exception cref="UnauthorizedAccessException">In case of access to the resources cannot be granted.</exception>
367         public void StartFocusing(bool continuous)
368         {
369             ValidateState(CameraState.Preview, CameraState.Captured);
370
371             Native.StartFocusing(_handle, continuous).ThrowIfFailed("Failed to cancel the camera focus.");
372         }
373
374         /// <summary>
375         /// Stops camera auto focusing.
376         /// The camera must be in the <see cref="CameraState.Preview"/> or the <see cref="CameraState.Captured"/> state.
377         /// </summary>
378         /// <since_tizen> 3 </since_tizen>
379         /// <privilege> http://tizen.org/privilege/camera </privilege>
380         /// <feature> http://tizen.org/feature/camera </feature>
381         /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
382         /// <exception cref="NotSupportedException">In case of this feature is not supported.</exception>
383         /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
384         /// <exception cref="UnauthorizedAccessException">In case of access to the resources cannot be granted.</exception>
385         public void StopFocusing()
386         {
387             ValidateState(CameraState.Preview, CameraState.Captured);
388
389             Native.CancelFocusing(_handle).ThrowIfFailed("Failed to cancel the camera focus.");
390         }
391
392         /// <summary>
393         /// Starts face detection.
394         /// The camera must be in the <see cref="CameraState.Preview"/> state.
395         /// </summary>
396         /// <since_tizen> 3 </since_tizen>
397         /// <privilege> http://tizen.org/privilege/camera </privilege>
398         /// <feature> http://tizen.org/feature/camera </feature>
399         /// <remarks>
400         /// This should be called after <see cref="StartPreview"/> is started.
401         /// The Eventhandler set using <see cref="FaceDetected"/> is invoked when the face is detected in the preview frame.
402         /// Internally, it starts continuously focus and focusing on the detected face.
403         /// </remarks>
404         /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
405         /// <exception cref="NotSupportedException">In case of this feature is not supported.</exception>
406         /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
407         /// <exception cref="UnauthorizedAccessException">In case of access to the resources cannot be granted.</exception>
408         public void StartFaceDetection()
409         {
410             ValidateState(CameraState.Preview);
411
412             _faceDetectedCallback = (IntPtr faces, int count, IntPtr userData) =>
413             {
414                 var result = new List<FaceDetectionData>();
415                 IntPtr current = faces;
416
417                 for (int i = 0; i < count; i++)
418                 {
419                     result.Add(new FaceDetectionData(current));
420                     current = IntPtr.Add(current, Marshal.SizeOf<Native.DetectedFaceStruct>());
421                 }
422
423                 FaceDetected?.Invoke(this, new FaceDetectedEventArgs(result));
424             };
425
426             Native.StartFaceDetection(_handle, _faceDetectedCallback).
427                 ThrowIfFailed("Failed to start face detection");
428         }
429
430         /// <summary>
431         /// Stops face detection.
432         /// </summary>
433         /// <since_tizen> 3 </since_tizen>
434         /// <privilege> http://tizen.org/privilege/camera </privilege>
435         /// <feature> http://tizen.org/feature/camera </feature>
436         /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
437         /// <exception cref="NotSupportedException">In case of this feature is not supported.</exception>
438         /// <exception cref="ObjectDisposedException">The camera already has been disposed of.</exception>
439         /// <exception cref="UnauthorizedAccessException">In case of access to the resources cannot be granted.</exception>
440         public void StopFaceDetection()
441         {
442             ValidateNotDisposed();
443
444             if (_faceDetectedCallback == null)
445             {
446                 throw new InvalidOperationException("The face detection is not started.");
447             }
448
449             Native.StopFaceDetection(_handle).ThrowIfFailed("Failed to stop the face detection.");
450
451             _faceDetectedCallback = null;
452         }
453         #endregion Methods
454     }
455 }