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.Collections.Generic;
19 using System.Diagnostics;
21 using System.Runtime.InteropServices;
23 namespace Tizen.Multimedia
25 static internal class RecorderLog
27 internal const string Tag = "Tizen.Multimedia.Recorder";
31 /// The recorder class provides methods to create audio/video recorder,
32 /// to start, stop and save the recorded content. It also provides methods
33 /// to get/set various attributes and capabilities of recorder.
36 /// http://tizen.org/privilege/recorder
38 public class Recorder : IDisposable
40 private IntPtr _handle = IntPtr.Zero;
41 private bool _disposed = false;
42 private RecorderState _state = RecorderState.None;
45 /// Audio recorder constructor.
48 /// http://tizen.org/privilege/microphone
52 RecorderErrorFactory.ThrowIfError(Interop.Recorder.Create(out _handle),
53 "Failed to create Audio recorder");
55 Feature = new RecorderFeatures(this);
56 Setting = new RecorderSettings(this);
60 SetState(RecorderState.Created);
64 /// Video recorder constructor.
66 /// <param name="camera">
67 /// The camera object.
70 /// http://tizen.org/privilege/camera
72 public Recorder(Camera camera)
74 RecorderErrorFactory.ThrowIfError(Interop.Recorder.CreateVideo(camera.GetHandle(), out _handle),
75 "Failed to create Video recorder.");
77 Feature = new RecorderFeatures(this);
78 Setting = new RecorderSettings(this);
82 SetState(RecorderState.Created);
86 /// Recorder destructor.
93 internal IntPtr GetHandle()
95 ValidateNotDisposed();
99 #region Dispose support
101 /// Release any unmanaged resources used by this object.
103 public void Dispose()
106 GC.SuppressFinalize(this);
109 protected virtual void Dispose(bool disposing)
115 // to be used if there are any other disposable objects
117 if (_handle != IntPtr.Zero)
119 Interop.Recorder.Destroy(_handle);
120 _handle = IntPtr.Zero;
126 internal void ValidateNotDisposed()
130 throw new ObjectDisposedException(nameof(Recorder));
133 #endregion Dispose support
135 #region Check recorder state
136 internal void ValidateState(params RecorderState[] required)
138 ValidateNotDisposed();
140 Debug.Assert(required.Length > 0);
142 var curState = _state;
143 if (!required.Contains(curState))
145 throw new InvalidOperationException($"The recorder is not in a valid state. " +
146 $"Current State : { curState }, Valid State : { string.Join(", ", required) }.");
150 internal void SetState(RecorderState state)
154 #endregion Check recorder state
156 #region EventHandlers
158 /// Event that occurs when an error occurs during recorder operation.
160 public event EventHandler<RecordingErrorOccurredEventArgs> ErrorOccurred;
161 private Interop.Recorder.RecorderErrorCallback _errorOccuredCallback;
164 /// Event that occurs when recorder is interrupted.
166 public event EventHandler<RecorderInterruptedEventArgs> Interrupted;
167 private Interop.Recorder.InterruptedCallback _interruptedCallback;
170 /// This event occurs when recorder state is changed.
172 public event EventHandler<RecorderStateChangedEventArgs> StateChanged;
173 private Interop.Recorder.StatechangedCallback _stateChangedCallback;
176 /// Event that occurs when recording information changes.
178 public event EventHandler<RecordingProgressEventArgs> RecordingProgress;
179 private Interop.Recorder.RecordingProgressCallback _recordingProgressCallback;
182 /// Event that occurs when audio stream data is being delivered.
184 public event EventHandler<AudioStreamDeliveredEventArgs> AudioStreamDelivered;
185 private Interop.Recorder.AudioStreamCallback _audioStreamCallback;
188 /// Event that occurs when recording limit is reached.
190 public event EventHandler<RecordingLimitReachedEventArgs> RecordingLimitReached;
191 private Interop.Recorder.RecordingLimitReachedCallback _recordingLimitReachedCallback;
194 /// Event that occurs when muxed stream data is being delivered.
196 public event EventHandler<MuxedStreamDeliveredEventArgs> MuxedStreamDelivered;
197 private Interop.Recorder.MuxedStreamCallback _muxedStreamCallback;
198 #endregion EventHandlers
202 /// Gets the various recorder features.
204 public RecorderFeatures Feature { get; }
207 /// Get/Set the various recorder settings.
209 public RecorderSettings Setting { get; }
212 /// The current state of the recorder.
214 /// <value>A <see cref="RecorderState"/> that specifies the state of recorder.</value>
215 /// <exception cref="ObjectDisposedException">The camera already has been disposed.</exception>
216 public RecorderState State
220 ValidateNotDisposed();
222 RecorderState val = 0;
224 RecorderErrorFactory.ThrowIfError(Interop.Recorder.GetState(_handle, out val),
225 "Failed to get recorder state.");
230 #endregion Properties
234 /// Prepare the media recorder for recording.
235 /// The recorder must be in the <see cref="RecorderState.Created"/> state.
236 /// After this method is finished without any exception,
237 /// The state of recorder will be changed to <see cref="RecorderState.Ready"/> state.
240 /// Before calling the function, it is required to set AudioEncoder,
241 /// videoencoder and fileformat properties of recorder.
244 /// http://tizen.org/privilege/camera or http://tizen.org/privilege/microphone
246 /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
247 /// <exception cref="ObjectDisposedException">The camera already has been disposed.</exception>
248 public void Prepare()
250 ValidateState(RecorderState.Created);
252 RecorderErrorFactory.ThrowIfError(Interop.Recorder.Prepare(_handle),
253 "Failed to prepare media recorder for recording");
255 SetState(RecorderState.Ready);
259 /// Resets the media recorder.
260 /// The recorder must be in the <see cref="RecorderState.Ready"/> state.
261 /// After this method is finished without any exception,
262 /// The state of recorder will be changed to <see cref="RecorderState.Created"/> state.
265 /// http://tizen.org/privilege/camera or http://tizen.org/privilege/microphone
267 /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
268 /// <exception cref="ObjectDisposedException">The camera already has been disposed.</exception>
269 public void Unprepare()
271 ValidateState(RecorderState.Ready);
273 RecorderErrorFactory.ThrowIfError(Interop.Recorder.Unprepare(_handle),
274 "Failed to reset the media recorder");
276 SetState(RecorderState.Created);
280 /// Starts the recording.
281 /// The recorder must be in the <see cref="RecorderState.Ready"/> state.
282 /// After this method is finished without any exception,
283 /// The state of recorder will be changed to <see cref="RecorderState.Recording"/> state.
286 /// If file path has been set to an existing file, this file is removed automatically and updated by new one.
287 /// In the video recorder, some preview format does not support record mode. It will return InvalidOperation error.
288 /// You should use default preview format or CameraPixelFormatNv12 in the record mode.
289 /// The filename should be set before this function is invoked.
292 /// http://tizen.org/privilege/camera or http://tizen.org/privilege/microphone
294 /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
295 /// <exception cref="ObjectDisposedException">The camera already has been disposed.</exception>
298 ValidateState(RecorderState.Ready);
300 RecorderErrorFactory.ThrowIfError(Interop.Recorder.Start(_handle),
301 "Failed to start the media recorder");
303 SetState(RecorderState.Recording);
307 /// Pause the recording.
308 /// The recorder must be in the <see cref="RecorderState.Recording"/> state.
309 /// After this method is finished without any exception,
310 /// The state of recorder will be changed to <see cref="RecorderState.Paused"/> state.
313 /// Recording can be resumed with Start().
316 /// http://tizen.org/privilege/camera or http://tizen.org/privilege/microphone
318 /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
319 /// <exception cref="ObjectDisposedException">The camera already has been disposed.</exception>
322 ValidateState(RecorderState.Recording);
324 RecorderErrorFactory.ThrowIfError(Interop.Recorder.Pause(_handle),
325 "Failed to pause the media recorder");
327 SetState(RecorderState.Paused);
331 /// Stops recording and saves the result.
332 /// The recorder must be in the <see cref="RecorderState.Recording"/> or <see cref="RecorderState.Paused"/> state.
333 /// After this method is finished without any exception,
334 /// The state of recorder will be changed to <see cref="RecorderState.Ready"/> state.
337 /// http://tizen.org/privilege/camera or http://tizen.org/privilege/microphone
339 /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
340 /// <exception cref="ObjectDisposedException">The camera already has been disposed.</exception>
343 ValidateState(RecorderState.Recording, RecorderState.Paused);
345 RecorderErrorFactory.ThrowIfError(Interop.Recorder.Commit(_handle),
346 "Failed to save the recorded content");
348 SetState(RecorderState.Ready);
352 /// Cancels the recording.
353 /// The recording data is discarded and not written in the recording file.
354 /// The recorder must be in the <see cref="RecorderState.Recording"/> or <see cref="RecorderState.Paused"/> state.
355 /// After this method is finished without any exception,
356 /// The state of recorder will be changed to <see cref="RecorderState.Ready"/> state.
359 /// http://tizen.org/privilege/camera or http://tizen.org/privilege/microphone
361 /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
362 /// <exception cref="ObjectDisposedException">The camera already has been disposed.</exception>
365 ValidateState(RecorderState.Recording, RecorderState.Paused);
367 RecorderErrorFactory.ThrowIfError(Interop.Recorder.Cancel(_handle),
368 "Failed to cancel the recording");
370 SetState(RecorderState.Ready);
374 /// Sets the audio stream policy.
376 /// <param name="policy">Policy.</param>
377 /// <exception cref="ObjectDisposedException">The camera already has been disposed.</exception>
378 public void SetAudioStreamPolicy(AudioStreamPolicy policy)
380 ValidateNotDisposed();
382 RecorderErrorFactory.ThrowIfError(Interop.Recorder.SetAudioStreamPolicy(_handle, policy.Handle),
383 "Failed to set audio stream policy");
387 #region Callback registrations
388 private void RegisterCallbacks()
390 RegisterErrorCallback();
391 RegisterInterruptedCallback();
392 RegisterStateChangedCallback();
393 RegisterRecordingProgressCallback();
394 RegisterAudioStreamDeliveredCallback();
395 RegisterRecordingLimitReachedEvent();
396 RegisterMuxedStreamEvent();
399 private void RegisterErrorCallback()
401 _errorOccuredCallback = (RecorderErrorCode error, RecorderState current, IntPtr userData) =>
403 ErrorOccurred?.Invoke(this, new RecordingErrorOccurredEventArgs(error, current));
405 RecorderErrorFactory.ThrowIfError(Interop.Recorder.SetErrorCallback(_handle, _errorOccuredCallback, IntPtr.Zero),
406 "Setting Error callback failed");
409 private void RegisterInterruptedCallback()
411 _interruptedCallback = (RecorderPolicy policy, RecorderState previous, RecorderState current, IntPtr userData) =>
413 Interrupted?.Invoke(this, new RecorderInterruptedEventArgs(policy, previous, current));
415 RecorderErrorFactory.ThrowIfError(Interop.Recorder.SetInterruptedCallback(_handle, _interruptedCallback, IntPtr.Zero),
416 "Setting Interrupted callback failed");
419 private void RegisterStateChangedCallback()
421 _stateChangedCallback = (RecorderState previous, RecorderState current, bool byPolicy, IntPtr userData) =>
424 Log.Info(RecorderLog.Tag, "Recorder state changed " + previous.ToString() + " -> " + current.ToString());
425 StateChanged?.Invoke(this, new RecorderStateChangedEventArgs(previous, current, byPolicy));
427 RecorderErrorFactory.ThrowIfError(Interop.Recorder.SetStateChangedCallback(_handle, _stateChangedCallback, IntPtr.Zero),
428 "Setting state changed callback failed");
431 private void RegisterRecordingProgressCallback()
433 _recordingProgressCallback = (ulong elapsedTime, ulong fileSize, IntPtr userData) =>
435 RecordingProgress?.Invoke(this, new RecordingProgressEventArgs(elapsedTime, fileSize));
437 RecorderErrorFactory.ThrowIfError(Interop.Recorder.SetRecordingProgressCallback(_handle, _recordingProgressCallback, IntPtr.Zero),
438 "Setting status changed callback failed");
441 private void RegisterAudioStreamDeliveredCallback()
443 _audioStreamCallback = (IntPtr stream, int streamSize, AudioSampleType type, int channel, uint recordingTime, IntPtr userData) =>
445 AudioStreamDelivered?.Invoke(this, new AudioStreamDeliveredEventArgs(stream, streamSize, type, channel, recordingTime));
447 RecorderErrorFactory.ThrowIfError(Interop.Recorder.SetAudioStreamCallback(_handle, _audioStreamCallback, IntPtr.Zero),
448 "Setting audiostream callback failed");
451 private void RegisterRecordingLimitReachedEvent()
453 _recordingLimitReachedCallback = (RecordingLimitType type, IntPtr userData) =>
455 RecordingLimitReached?.Invoke(this, new RecordingLimitReachedEventArgs(type));
457 RecorderErrorFactory.ThrowIfError(Interop.Recorder.SetLimitReachedCallback(_handle, _recordingLimitReachedCallback, IntPtr.Zero),
458 "Setting limit reached callback failed");
461 private void RegisterMuxedStreamEvent()
463 _muxedStreamCallback = (IntPtr stream, int streamSize, ulong offset, IntPtr userData) =>
465 MuxedStreamDelivered?.Invoke(this, new MuxedStreamDeliveredEventArgs(stream, streamSize, offset));
467 RecorderErrorFactory.ThrowIfError(Interop.Recorder.SetMuxedStreamCallback(_handle, _muxedStreamCallback, IntPtr.Zero),
468 "Setting muxed stream callback failed");
470 #endregion Callback registrations