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 Native = Interop.Recorder;
22 namespace Tizen.Multimedia
24 static internal class RecorderLog
26 internal const string Tag = "Tizen.Multimedia.Recorder";
30 /// The recorder class provides methods to create audio/video recorder,
31 /// to start, stop and save the recorded content. It also provides methods
32 /// to get/set various attributes and capabilities of recorder.
34 public class Recorder : IDisposable
36 private IntPtr _handle = IntPtr.Zero;
37 private bool _disposed = false;
38 private RecorderState _state = RecorderState.None;
41 /// Audio recorder constructor.
45 RecorderErrorFactory.ThrowIfError(Native.Create(out _handle),
46 "Failed to create Audio recorder");
48 Feature = new RecorderFeatures(this);
49 Setting = new RecorderSettings(this);
53 SetState(RecorderState.Created);
57 /// Video recorder constructor.
59 /// <param name="camera">
60 /// The camera object.
62 public Recorder(Camera camera)
64 RecorderErrorFactory.ThrowIfError(Native.CreateVideo(camera.Handle, out _handle),
65 "Failed to create Video recorder.");
67 Feature = new RecorderFeatures(this);
68 Setting = new RecorderSettings(this);
72 SetState(RecorderState.Created);
76 /// Recorder destructor.
83 internal IntPtr GetHandle()
85 ValidateNotDisposed();
89 #region Dispose support
91 /// Releases the unmanaged resources used by the Recorder.
93 /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
94 protected virtual void Dispose(bool disposing)
100 // to be used if there are any other disposable objects
102 if (_handle != IntPtr.Zero)
104 Native.Destroy(_handle);
105 _handle = IntPtr.Zero;
112 /// Releases all resources used by the Recorder.
114 /// <since_tizen> 3 </since_tizen>
115 public void Dispose()
118 GC.SuppressFinalize(this);
121 internal void ValidateNotDisposed()
125 throw new ObjectDisposedException(nameof(Recorder));
128 #endregion Dispose support
130 #region Check recorder state
131 internal void ValidateState(params RecorderState[] required)
133 ValidateNotDisposed();
135 Debug.Assert(required.Length > 0);
137 var curState = _state;
138 if (!required.Contains(curState))
140 throw new InvalidOperationException($"The recorder is not in a valid state. " +
141 $"Current State : { curState }, Valid State : { string.Join(", ", required) }.");
145 internal void SetState(RecorderState state)
149 #endregion Check recorder state
151 #region EventHandlers
153 /// Event that occurs when an error occurs during recorder operation.
155 /// <since_tizen> 3 </since_tizen>
156 public event EventHandler<RecordingErrorOccurredEventArgs> ErrorOccurred;
157 private Native.RecorderErrorCallback _errorOccuredCallback;
160 /// Event that occurs when recorder is interrupted.
162 /// <since_tizen> 3 </since_tizen>
163 public event EventHandler<RecorderInterruptedEventArgs> Interrupted;
164 private Native.InterruptedCallback _interruptedCallback;
167 /// This event occurs when recorder state is changed.
169 /// <since_tizen> 3 </since_tizen>
170 public event EventHandler<RecorderStateChangedEventArgs> StateChanged;
171 private Native.StatechangedCallback _stateChangedCallback;
174 /// Event that occurs when recording information changes.
176 /// <since_tizen> 3 </since_tizen>
177 public event EventHandler<RecordingProgressEventArgs> RecordingProgress;
178 private Native.RecordingProgressCallback _recordingProgressCallback;
181 /// Event that occurs when audio stream data is being delivered.
183 /// <since_tizen> 3 </since_tizen>
184 public event EventHandler<AudioStreamDeliveredEventArgs> AudioStreamDelivered;
185 private Native.AudioStreamCallback _audioStreamCallback;
188 /// Event that occurs when recording limit is reached.
190 /// <since_tizen> 3 </since_tizen>
191 public event EventHandler<RecordingLimitReachedEventArgs> RecordingLimitReached;
192 private Native.RecordingLimitReachedCallback _recordingLimitReachedCallback;
195 /// Event that occurs when muxed stream data is being delivered.
197 /// <since_tizen> 3 </since_tizen>
198 public event EventHandler<MuxedStreamDeliveredEventArgs> MuxedStreamDelivered;
199 private Native.MuxedStreamCallback _muxedStreamCallback;
200 #endregion EventHandlers
204 /// Gets the various recorder features.
206 /// <since_tizen> 3 </since_tizen>
207 public RecorderFeatures Feature { get; }
210 /// Get/Set the various recorder settings.
212 /// <since_tizen> 3 </since_tizen>
213 public RecorderSettings Setting { get; }
216 /// The current state of the recorder.
218 /// <since_tizen> 3 </since_tizen>
219 /// <value>A <see cref="RecorderState"/> that specifies the state of recorder.</value>
220 /// <exception cref="ObjectDisposedException">The camera already has been disposed.</exception>
221 public RecorderState State
225 ValidateNotDisposed();
227 RecorderState val = 0;
229 RecorderErrorFactory.ThrowIfError(Native.GetState(_handle, out val),
230 "Failed to get recorder state.");
235 #endregion Properties
239 /// Prepare the media recorder for recording.
240 /// The recorder must be in the <see cref="RecorderState.Created"/> state.
241 /// After this method is finished without any exception,
242 /// The state of recorder will be changed to <see cref="RecorderState.Ready"/> state.
244 /// <since_tizen> 3 </since_tizen>
246 /// Before calling the function, it is required to set AudioEncoder,
247 /// videoencoder and fileformat properties of recorder.
249 /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
250 /// <exception cref="ObjectDisposedException">The camera already has been disposed.</exception>
251 public void Prepare()
253 ValidateState(RecorderState.Created);
255 RecorderErrorFactory.ThrowIfError(Native.Prepare(_handle),
256 "Failed to prepare media recorder for recording");
258 SetState(RecorderState.Ready);
262 /// Resets the media recorder.
263 /// The recorder must be in the <see cref="RecorderState.Ready"/> state.
264 /// After this method is finished without any exception,
265 /// The state of recorder will be changed to <see cref="RecorderState.Created"/> state.
267 /// <since_tizen> 3 </since_tizen>
268 /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
269 /// <exception cref="ObjectDisposedException">The camera already has been disposed.</exception>
270 public void Unprepare()
272 ValidateState(RecorderState.Ready);
274 RecorderErrorFactory.ThrowIfError(Native.Unprepare(_handle),
275 "Failed to reset the media recorder");
277 SetState(RecorderState.Created);
281 /// Starts the recording.
282 /// The recorder must be in the <see cref="RecorderState.Ready"/> state.
283 /// After this method is finished without any exception,
284 /// The state of recorder will be changed to <see cref="RecorderState.Recording"/> state.
286 /// <since_tizen> 3 </since_tizen>
288 /// If file path has been set to an existing file, this file is removed automatically and updated by new one.
289 /// In the video recorder, some preview format does not support record mode. It will return InvalidOperation error.
290 /// You should use default preview format or CameraPixelFormatNv12 in the record mode.
291 /// The filename should be set before this function is invoked.
294 /// http://tizen.org/privilege/recorder
296 /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
297 /// <exception cref="ObjectDisposedException">The camera already has been disposed.</exception>
298 /// <exception cref="UnauthorizedAccessException">In case of access to the resources cannot be granted.</exception>
301 ValidateState(RecorderState.Ready, RecorderState.Paused);
303 RecorderErrorFactory.ThrowIfError(Native.Start(_handle),
304 "Failed to start the media recorder");
306 SetState(RecorderState.Recording);
310 /// Pause the recording.
311 /// The recorder must be in the <see cref="RecorderState.Recording"/> state.
312 /// After this method is finished without any exception,
313 /// The state of recorder will be changed to <see cref="RecorderState.Paused"/> state.
315 /// <since_tizen> 3 </since_tizen>
317 /// Recording can be resumed with Start().
320 /// http://tizen.org/privilege/recorder
322 /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
323 /// <exception cref="ObjectDisposedException">The camera already has been disposed.</exception>
324 /// <exception cref="UnauthorizedAccessException">In case of access to the resources cannot be granted.</exception>
327 ValidateState(RecorderState.Recording);
329 RecorderErrorFactory.ThrowIfError(Native.Pause(_handle),
330 "Failed to pause the media recorder");
332 SetState(RecorderState.Paused);
336 /// Stops recording and saves the result.
337 /// The recorder must be in the <see cref="RecorderState.Recording"/> or <see cref="RecorderState.Paused"/> state.
338 /// After this method is finished without any exception,
339 /// The state of recorder will be changed to <see cref="RecorderState.Ready"/> state.
341 /// <since_tizen> 3 </since_tizen>
343 /// http://tizen.org/privilege/recorder
345 /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
346 /// <exception cref="ObjectDisposedException">The camera already has been disposed.</exception>
347 /// <exception cref="UnauthorizedAccessException">In case of access to the resources cannot be granted.</exception>
350 ValidateState(RecorderState.Recording, RecorderState.Paused);
352 RecorderErrorFactory.ThrowIfError(Native.Commit(_handle),
353 "Failed to save the recorded content");
355 SetState(RecorderState.Ready);
359 /// Cancels the recording.
360 /// The recording data is discarded and not written in the recording file.
361 /// The recorder must be in the <see cref="RecorderState.Recording"/> or <see cref="RecorderState.Paused"/> state.
362 /// After this method is finished without any exception,
363 /// The state of recorder will be changed to <see cref="RecorderState.Ready"/> state.
365 /// <since_tizen> 3 </since_tizen>
367 /// http://tizen.org/privilege/recorder
369 /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
370 /// <exception cref="ObjectDisposedException">The camera already has been disposed.</exception>
371 /// <exception cref="UnauthorizedAccessException">In case of access to the resources cannot be granted.</exception>
374 ValidateState(RecorderState.Recording, RecorderState.Paused);
376 RecorderErrorFactory.ThrowIfError(Native.Cancel(_handle),
377 "Failed to cancel the recording");
379 SetState(RecorderState.Ready);
383 /// Sets the audio stream policy.
385 /// <since_tizen> 3 </since_tizen>
386 /// <param name="policy">Policy.</param>
387 /// <exception cref="ObjectDisposedException">The camera already has been disposed.</exception>
388 public void SetAudioStreamPolicy(AudioStreamPolicy policy)
390 ValidateNotDisposed();
392 RecorderErrorFactory.ThrowIfError(Native.SetAudioStreamPolicy(_handle, policy.Handle),
393 "Failed to set audio stream policy");
397 #region Callback registrations
398 private void RegisterCallbacks()
400 RegisterErrorCallback();
401 RegisterInterruptedCallback();
402 RegisterStateChangedCallback();
403 RegisterRecordingProgressCallback();
404 RegisterAudioStreamDeliveredCallback();
405 RegisterRecordingLimitReachedEvent();
406 RegisterMuxedStreamEvent();
409 private void RegisterErrorCallback()
411 _errorOccuredCallback = (RecorderErrorCode error, RecorderState current, IntPtr userData) =>
413 ErrorOccurred?.Invoke(this, new RecordingErrorOccurredEventArgs(error, current));
415 RecorderErrorFactory.ThrowIfError(Native.SetErrorCallback(_handle, _errorOccuredCallback, IntPtr.Zero),
416 "Setting Error callback failed");
419 private void RegisterInterruptedCallback()
421 _interruptedCallback = (RecorderPolicy policy, RecorderState previous, RecorderState current, IntPtr userData) =>
423 Interrupted?.Invoke(this, new RecorderInterruptedEventArgs(policy, previous, current));
425 RecorderErrorFactory.ThrowIfError(Native.SetInterruptedCallback(_handle, _interruptedCallback, IntPtr.Zero),
426 "Setting Interrupted callback failed");
429 private void RegisterStateChangedCallback()
431 _stateChangedCallback = (RecorderState previous, RecorderState current, bool byPolicy, IntPtr userData) =>
434 Log.Info(RecorderLog.Tag, "Recorder state changed " + previous.ToString() + " -> " + current.ToString());
435 StateChanged?.Invoke(this, new RecorderStateChangedEventArgs(previous, current, byPolicy));
437 RecorderErrorFactory.ThrowIfError(Native.SetStateChangedCallback(_handle, _stateChangedCallback, IntPtr.Zero),
438 "Setting state changed callback failed");
441 private void RegisterRecordingProgressCallback()
443 _recordingProgressCallback = (ulong elapsedTime, ulong fileSize, IntPtr userData) =>
445 RecordingProgress?.Invoke(this, new RecordingProgressEventArgs(elapsedTime, fileSize));
447 RecorderErrorFactory.ThrowIfError(Native.SetRecordingProgressCallback(_handle, _recordingProgressCallback, IntPtr.Zero),
448 "Setting status changed callback failed");
451 private void RegisterAudioStreamDeliveredCallback()
453 _audioStreamCallback = (IntPtr stream, int streamSize, AudioSampleType type, int channel, uint recordingTime, IntPtr userData) =>
455 AudioStreamDelivered?.Invoke(this, new AudioStreamDeliveredEventArgs(stream, streamSize, type, channel, recordingTime));
457 RecorderErrorFactory.ThrowIfError(Native.SetAudioStreamCallback(_handle, _audioStreamCallback, IntPtr.Zero),
458 "Setting audiostream callback failed");
461 private void RegisterRecordingLimitReachedEvent()
463 _recordingLimitReachedCallback = (RecordingLimitType type, IntPtr userData) =>
465 RecordingLimitReached?.Invoke(this, new RecordingLimitReachedEventArgs(type));
467 RecorderErrorFactory.ThrowIfError(Native.SetLimitReachedCallback(_handle, _recordingLimitReachedCallback, IntPtr.Zero),
468 "Setting limit reached callback failed");
471 private void RegisterMuxedStreamEvent()
473 _muxedStreamCallback = (IntPtr stream, int streamSize, ulong offset, IntPtr userData) =>
475 MuxedStreamDelivered?.Invoke(this, new MuxedStreamDeliveredEventArgs(stream, streamSize, offset));
477 RecorderErrorFactory.ThrowIfError(Native.SetMuxedStreamCallback(_handle, _muxedStreamCallback, IntPtr.Zero),
478 "Setting muxed stream callback failed");
480 #endregion Callback registrations