2 * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 using System.Diagnostics;
20 using System.Threading;
21 using Native = Interop.Recorder;
22 using NativeHandle = Interop.RecorderHandle;
24 namespace Tizen.Multimedia
27 /// Recorder is a base class for audio and video recorders that
28 /// provides the ability to control the recording of a multimedia content.<br/>
30 /// Simple audio and audio/video are supported.
32 public abstract partial class Recorder : IDisposable
34 private readonly NativeHandle _handle;
35 private RecorderState _state;
36 private ThreadLocal<bool> _isInAudioStreamStoring = new ThreadLocal<bool>();
38 internal Recorder(NativeHandle handle)
55 internal NativeHandle Handle
61 throw new ObjectDisposedException(nameof(Recorder));
68 #region Dispose support
69 private bool _disposed;
72 /// Releases the unmanaged resources used by the Recorder.
74 /// <since_tizen> 3 </since_tizen>
81 /// Releases the resources used by the Recorder.
83 /// <param name="disposing">
84 /// true to release both managed and unmanaged resources; false to release only unmanaged resources.
86 /// <since_tizen> 3 </since_tizen>
87 protected virtual void Dispose(bool disposing)
99 #endregion Dispose support
101 #region State validation
102 internal void ValidateState(params RecorderState[] required)
104 Debug.Assert(required.Length > 0);
106 var curState = _state;
107 if (!required.Contains(curState))
109 throw new InvalidOperationException("The recorder is not in a valid state. " +
110 $"Current State : { curState }, Valid State : { string.Join(", ", required) }.");
114 private void SetState(RecorderState state)
123 /// Gets the current state of the recorder.
125 /// <value>A <see cref="RecorderState"/> that specifies the state of the recorder.</value>
126 /// <exception cref="ObjectDisposedException">The recorder already has been disposed of.</exception>
127 /// <since_tizen> 3 </since_tizen>
128 public RecorderState State
132 Native.GetState(Handle, out var val).ThrowIfError("Failed to get recorder state.");
137 #endregion Properties
141 /// Prepares the media recorder for recording.
144 /// The recorder should be in the <see cref="RecorderState.Idle"/> state.
145 /// The state of the recorder will be the <see cref="RecorderState.Ready"/> after this.
146 /// It has no effect if the current state is the <see cref="RecorderState.Ready"/>.
148 /// <exception cref="InvalidOperationException">
149 /// The recorder is not in the valid state.<br/>
151 /// An internal error occurred.
153 /// <exception cref="ObjectDisposedException">The recorder already has been disposed of.</exception>
154 /// <since_tizen> 3 </since_tizen>
155 public void Prepare()
157 if (_state == RecorderState.Ready)
162 ValidateState(RecorderState.Idle);
164 Native.Prepare(Handle).ThrowIfError("Failed to prepare media recorder");
166 SetState(RecorderState.Ready);
169 private void ThrowIfAccessedInAudioStreamStoring()
171 if (_isInAudioStreamStoring.Value)
173 throw new InvalidOperationException("The method can't be called in the AudioStreamStoring event");
178 /// Resets the media recorder.
181 /// The recorder should be in the <see cref="RecorderState.Ready"/> state.
182 /// The state of recorder will be the <see cref="RecorderState.Idle"/> after this.
183 /// It has no effect if the current state is the <see cref="RecorderState.Idle"/>.
185 /// <exception cref="InvalidOperationException">
186 /// The recorder is not in the valid state.<br/>
188 /// An internal error occurred.
190 /// <exception cref="ObjectDisposedException">The recorder already has been disposed of.</exception>
191 /// <since_tizen> 3 </since_tizen>
192 public void Unprepare()
194 ThrowIfAccessedInAudioStreamStoring();
196 if (_state == RecorderState.Idle)
201 ValidateState(RecorderState.Ready);
203 Native.Unprepare(Handle).ThrowIfError("Failed to reset the media recorder");
205 SetState(RecorderState.Idle);
209 /// Starts the recording.
212 /// The recorder must be in the <see cref="RecorderState.Ready"/> state.
213 /// The state of the recorder will be the <see cref="RecorderState.Recording"/> after this. <br/>
215 /// If the specified path exists, the file is removed automatically and updated by new one.<br/>
216 /// The mediastorage privilege(http://tizen.org/privilege/mediastorage) is required if the path is relevant to media storage.<br/>
217 /// The externalstorage privilege(http://tizen.org/privilege/externalstorage) is required if the path is relevant to external storage.<br/>
219 /// In the video recorder, some preview format does not support record mode.
220 /// You should use the default preview format or the <see cref="CameraPixelFormat.Nv12"/> in the record mode.
222 /// <param name="savePath">The file path for recording result.</param>
223 /// <privilege>http://tizen.org/privilege/recorder</privilege>
224 /// <exception cref="InvalidOperationException">
225 /// The recorder is not in the valid state.<br/>
227 /// The preview format of the camera is not supported.<br/>
229 /// An internal error occurred.
231 /// <exception cref="ObjectDisposedException">The recorder already has been disposed of.</exception>
232 /// <exception cref="ArgumentNullException"><paramref name="savePath"/> is null.</exception>
233 /// <exception cref="ArgumentException"><paramref name="savePath"/> is a zero-length string, contains only white space.</exception>
234 /// <exception cref="UnauthorizedAccessException">Caller does not have required privilege.</exception>
235 /// <seealso cref="Commit"/>
236 /// <seealso cref="Cancel"/>
237 /// <since_tizen> 4 </since_tizen>
238 public void Start(string savePath)
240 ValidateState(RecorderState.Ready);
242 if (savePath == null)
244 throw new ArgumentNullException(nameof(savePath));
247 if (string.IsNullOrWhiteSpace(savePath))
249 throw new ArgumentException($"{nameof(savePath)} is an empty string.", nameof(savePath));
252 Native.SetFileName(Handle, savePath).ThrowIfError("Failed to set save path.");
254 Native.Start(Handle).ThrowIfError("Failed to start the media recorder");
256 SetState(RecorderState.Recording);
260 /// Resumes the recording.
263 /// The recorder should be in the <see cref="RecorderState.Paused"/> state.
264 /// The state of recorder will be the <see cref="RecorderState.Recording"/> after this.
265 /// It has no effect if the current state is the <see cref="RecorderState.Recording"/>.
267 /// <exception cref="InvalidOperationException">
268 /// The recorder is not in the valid state.<br/>
270 /// An internal error occurred.
272 /// <exception cref="ObjectDisposedException">The recorder already has been disposed of.</exception>
273 /// <since_tizen> 4 </since_tizen>
276 if (_state == RecorderState.Recording)
281 ValidateState(RecorderState.Paused);
283 Native.Start(Handle).ThrowIfError("Failed to resume the media recorder");
285 SetState(RecorderState.Recording);
289 /// Pauses the recording.
292 /// The recorder should be in the <see cref="RecorderState.Recording"/> state.
293 /// The state of the recorder will be the <see cref="RecorderState.Paused"/> after this.
294 /// It has no effect if the current state is the <see cref="RecorderState.Paused"/>.
296 /// <exception cref="InvalidOperationException">
297 /// The recorder is not in the valid state.<br/>
299 /// An internal error occurred.
301 /// <exception cref="ObjectDisposedException">The recorder already has been disposed of.</exception>
302 /// <since_tizen> 3 </since_tizen>
305 if (_state == RecorderState.Paused)
310 ValidateState(RecorderState.Recording);
312 Native.Pause(Handle).ThrowIfError("Failed to pause the media recorder");
314 SetState(RecorderState.Paused);
318 /// Stops recording and saves the result.
321 /// The recorder must be in the <see cref="RecorderState.Recording"/> or the <see cref="RecorderState.Paused"/> state.
322 /// The state of the recorder will be the <see cref="RecorderState.Ready"/> after the operation.
324 /// <exception cref="InvalidOperationException">
325 /// The recorder is not in the valid state.<br/>
327 /// The method is called in <see cref="AudioStreamStoring"/> event.<br/>
329 /// An internal error occurred.
331 /// <exception cref="ObjectDisposedException">The recorder already has been disposed of.</exception>
332 /// <since_tizen> 3 </since_tizen>
335 ThrowIfAccessedInAudioStreamStoring();
337 ValidateState(RecorderState.Recording, RecorderState.Paused);
339 Native.Commit(Handle).ThrowIfError("Failed to save the recorded content");
341 SetState(RecorderState.Ready);
345 /// Cancels the recording.<br/>
346 /// The recording data is discarded and not written in the recording file.
349 /// The recorder must be in the <see cref="RecorderState.Recording"/> or the <see cref="RecorderState.Paused"/> state.
350 /// The state of the recorder will be the <see cref="RecorderState.Ready"/> after the operation.
352 /// <exception cref="InvalidOperationException">
353 /// The recorder is not in the valid state.<br/>
355 /// The method is called in <see cref="AudioStreamStoring"/> event.<br/>
357 /// An internal error occurred.
359 /// <exception cref="ObjectDisposedException">The recorder already has been disposed of.</exception>
360 /// <since_tizen> 3 </since_tizen>
363 ThrowIfAccessedInAudioStreamStoring();
365 ValidateState(RecorderState.Recording, RecorderState.Paused);
367 Native.Cancel(Handle).ThrowIfError("Failed to cancel the recording");
369 SetState(RecorderState.Ready);
373 /// Apply the audio stream policy.
376 /// The recorder must be in the <see cref="RecorderState.Idle"/> or the <see cref="RecorderState.Ready"/> state.
378 /// <param name="policy">The policy to apply.</param>
379 /// <exception cref="ArgumentNullException"><paramref name="policy"/> is null.</exception>
380 /// <exception cref="InvalidOperationException">
381 /// The recorder is not in the valid state.<br/>
383 /// <paramref name="policy"/> is not supported for the recorder.<br/>
385 /// An internal error occurred.
387 /// <exception cref="ObjectDisposedException">
388 /// The recorder already has been disposed of.<br/>
390 /// <paramref name="policy"/> already has been disposed of.
392 /// <since_tizen> 4 </since_tizen>
393 public void ApplyAudioStreamPolicy(AudioStreamPolicy policy)
397 throw new ArgumentNullException(nameof(policy));
400 ValidateState(RecorderState.Idle, RecorderState.Ready);
402 Native.SetAudioStreamPolicy(Handle, policy.Handle).ThrowIfError("Failed to apply the audio stream policy.");
406 /// Returns the peak audio input level in dB since the last call to this method.
409 /// 0dB indicates the maximum input level, -300dB indicates the minimum input level.<br/>
411 /// The recorder must be in the <see cref="RecorderState.Recording"/> or the <see cref="RecorderState.Paused"/> state.
413 /// <exception cref="ObjectDisposedException">The recorder already has been disposed of.</exception>
414 /// <since_tizen> 4 </since_tizen>
415 public double GetPeakAudioLevel()
417 ValidateState(RecorderState.Recording, RecorderState.Paused);
419 Native.GetAudioLevel(Handle, out var level).ThrowIfError("Failed to get audio level.");
425 /// Returns the state of recorder device.
427 /// <exception cref="ArgumentException"><paramref name="type"/> is invalid.</exception>
428 /// <since_tizen> 4 </since_tizen>
429 public static RecorderDeviceState GetDeviceState(RecorderType type)
431 ValidationUtil.ValidateEnum(typeof(RecorderType), type, nameof(type));
433 Native.GetDeviceState(type, out var state).ThrowIfError("Failed to get device state");