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 /// Release any unmanaged resources used by this object.
96 GC.SuppressFinalize(this);
99 protected virtual void Dispose(bool disposing)
105 // to be used if there are any other disposable objects
107 if (_handle != IntPtr.Zero)
109 Native.Destroy(_handle);
110 _handle = IntPtr.Zero;
116 internal void ValidateNotDisposed()
120 throw new ObjectDisposedException(nameof(Recorder));
123 #endregion Dispose support
125 #region Check recorder state
126 internal void ValidateState(params RecorderState[] required)
128 ValidateNotDisposed();
130 Debug.Assert(required.Length > 0);
132 var curState = _state;
133 if (!required.Contains(curState))
135 throw new InvalidOperationException($"The recorder is not in a valid state. " +
136 $"Current State : { curState }, Valid State : { string.Join(", ", required) }.");
140 internal void SetState(RecorderState state)
144 #endregion Check recorder state
146 #region EventHandlers
148 /// Event that occurs when an error occurs during recorder operation.
150 public event EventHandler<RecordingErrorOccurredEventArgs> ErrorOccurred;
151 private Native.RecorderErrorCallback _errorOccuredCallback;
154 /// Event that occurs when recorder is interrupted.
156 public event EventHandler<RecorderInterruptedEventArgs> Interrupted;
157 private Native.InterruptedCallback _interruptedCallback;
160 /// This event occurs when recorder state is changed.
162 public event EventHandler<RecorderStateChangedEventArgs> StateChanged;
163 private Native.StatechangedCallback _stateChangedCallback;
166 /// Event that occurs when recording information changes.
168 public event EventHandler<RecordingProgressEventArgs> RecordingProgress;
169 private Native.RecordingProgressCallback _recordingProgressCallback;
172 /// Event that occurs when audio stream data is being delivered.
174 public event EventHandler<AudioStreamDeliveredEventArgs> AudioStreamDelivered;
175 private Native.AudioStreamCallback _audioStreamCallback;
178 /// Event that occurs when recording limit is reached.
180 public event EventHandler<RecordingLimitReachedEventArgs> RecordingLimitReached;
181 private Native.RecordingLimitReachedCallback _recordingLimitReachedCallback;
184 /// Event that occurs when muxed stream data is being delivered.
186 public event EventHandler<MuxedStreamDeliveredEventArgs> MuxedStreamDelivered;
187 private Native.MuxedStreamCallback _muxedStreamCallback;
188 #endregion EventHandlers
192 /// Gets the various recorder features.
194 public RecorderFeatures Feature { get; }
197 /// Get/Set the various recorder settings.
199 public RecorderSettings Setting { get; }
202 /// The current state of the recorder.
204 /// <value>A <see cref="RecorderState"/> that specifies the state of recorder.</value>
205 /// <exception cref="ObjectDisposedException">The camera already has been disposed.</exception>
206 public RecorderState State
210 ValidateNotDisposed();
212 RecorderState val = 0;
214 RecorderErrorFactory.ThrowIfError(Native.GetState(_handle, out val),
215 "Failed to get recorder state.");
220 #endregion Properties
224 /// Prepare the media recorder for recording.
225 /// The recorder must be in the <see cref="RecorderState.Created"/> state.
226 /// After this method is finished without any exception,
227 /// The state of recorder will be changed to <see cref="RecorderState.Ready"/> state.
230 /// Before calling the function, it is required to set AudioEncoder,
231 /// videoencoder and fileformat properties of recorder.
233 /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
234 /// <exception cref="ObjectDisposedException">The camera already has been disposed.</exception>
235 public void Prepare()
237 ValidateState(RecorderState.Created);
239 RecorderErrorFactory.ThrowIfError(Native.Prepare(_handle),
240 "Failed to prepare media recorder for recording");
242 SetState(RecorderState.Ready);
246 /// Resets the media recorder.
247 /// The recorder must be in the <see cref="RecorderState.Ready"/> state.
248 /// After this method is finished without any exception,
249 /// The state of recorder will be changed to <see cref="RecorderState.Created"/> state.
251 /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
252 /// <exception cref="ObjectDisposedException">The camera already has been disposed.</exception>
253 public void Unprepare()
255 ValidateState(RecorderState.Ready);
257 RecorderErrorFactory.ThrowIfError(Native.Unprepare(_handle),
258 "Failed to reset the media recorder");
260 SetState(RecorderState.Created);
264 /// Starts the recording.
265 /// The recorder must be in the <see cref="RecorderState.Ready"/> state.
266 /// After this method is finished without any exception,
267 /// The state of recorder will be changed to <see cref="RecorderState.Recording"/> state.
270 /// If file path has been set to an existing file, this file is removed automatically and updated by new one.
271 /// In the video recorder, some preview format does not support record mode. It will return InvalidOperation error.
272 /// You should use default preview format or CameraPixelFormatNv12 in the record mode.
273 /// The filename should be set before this function is invoked.
276 /// http://tizen.org/privilege/recorder
278 /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
279 /// <exception cref="ObjectDisposedException">The camera already has been disposed.</exception>
280 /// <exception cref="UnauthorizedAccessException">In case of access to the resources cannot be granted.</exception>
283 ValidateState(RecorderState.Ready, RecorderState.Paused);
285 RecorderErrorFactory.ThrowIfError(Native.Start(_handle),
286 "Failed to start the media recorder");
288 SetState(RecorderState.Recording);
292 /// Pause the recording.
293 /// The recorder must be in the <see cref="RecorderState.Recording"/> state.
294 /// After this method is finished without any exception,
295 /// The state of recorder will be changed to <see cref="RecorderState.Paused"/> state.
298 /// Recording can be resumed with Start().
301 /// http://tizen.org/privilege/recorder
303 /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
304 /// <exception cref="ObjectDisposedException">The camera already has been disposed.</exception>
305 /// <exception cref="UnauthorizedAccessException">In case of access to the resources cannot be granted.</exception>
308 ValidateState(RecorderState.Recording);
310 RecorderErrorFactory.ThrowIfError(Native.Pause(_handle),
311 "Failed to pause the media recorder");
313 SetState(RecorderState.Paused);
317 /// Stops recording and saves the result.
318 /// The recorder must be in the <see cref="RecorderState.Recording"/> or <see cref="RecorderState.Paused"/> state.
319 /// After this method is finished without any exception,
320 /// The state of recorder will be changed to <see cref="RecorderState.Ready"/> state.
323 /// http://tizen.org/privilege/recorder
325 /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
326 /// <exception cref="ObjectDisposedException">The camera already has been disposed.</exception>
327 /// <exception cref="UnauthorizedAccessException">In case of access to the resources cannot be granted.</exception>
330 ValidateState(RecorderState.Recording, RecorderState.Paused);
332 RecorderErrorFactory.ThrowIfError(Native.Commit(_handle),
333 "Failed to save the recorded content");
335 SetState(RecorderState.Ready);
339 /// Cancels the recording.
340 /// The recording data is discarded and not written in the recording file.
341 /// The recorder must be in the <see cref="RecorderState.Recording"/> or <see cref="RecorderState.Paused"/> state.
342 /// After this method is finished without any exception,
343 /// The state of recorder will be changed to <see cref="RecorderState.Ready"/> state.
346 /// http://tizen.org/privilege/recorder
348 /// <exception cref="InvalidOperationException">In case of any invalid operations.</exception>
349 /// <exception cref="ObjectDisposedException">The camera already has been disposed.</exception>
350 /// <exception cref="UnauthorizedAccessException">In case of access to the resources cannot be granted.</exception>
353 ValidateState(RecorderState.Recording, RecorderState.Paused);
355 RecorderErrorFactory.ThrowIfError(Native.Cancel(_handle),
356 "Failed to cancel the recording");
358 SetState(RecorderState.Ready);
362 /// Sets the audio stream policy.
364 /// <param name="policy">Policy.</param>
365 /// <exception cref="ObjectDisposedException">The camera already has been disposed.</exception>
366 public void SetAudioStreamPolicy(AudioStreamPolicy policy)
368 ValidateNotDisposed();
370 RecorderErrorFactory.ThrowIfError(Native.SetAudioStreamPolicy(_handle, policy.Handle),
371 "Failed to set audio stream policy");
375 #region Callback registrations
376 private void RegisterCallbacks()
378 RegisterErrorCallback();
379 RegisterInterruptedCallback();
380 RegisterStateChangedCallback();
381 RegisterRecordingProgressCallback();
382 RegisterAudioStreamDeliveredCallback();
383 RegisterRecordingLimitReachedEvent();
384 RegisterMuxedStreamEvent();
387 private void RegisterErrorCallback()
389 _errorOccuredCallback = (RecorderErrorCode error, RecorderState current, IntPtr userData) =>
391 ErrorOccurred?.Invoke(this, new RecordingErrorOccurredEventArgs(error, current));
393 RecorderErrorFactory.ThrowIfError(Native.SetErrorCallback(_handle, _errorOccuredCallback, IntPtr.Zero),
394 "Setting Error callback failed");
397 private void RegisterInterruptedCallback()
399 _interruptedCallback = (RecorderPolicy policy, RecorderState previous, RecorderState current, IntPtr userData) =>
401 Interrupted?.Invoke(this, new RecorderInterruptedEventArgs(policy, previous, current));
403 RecorderErrorFactory.ThrowIfError(Native.SetInterruptedCallback(_handle, _interruptedCallback, IntPtr.Zero),
404 "Setting Interrupted callback failed");
407 private void RegisterStateChangedCallback()
409 _stateChangedCallback = (RecorderState previous, RecorderState current, bool byPolicy, IntPtr userData) =>
412 Log.Info(RecorderLog.Tag, "Recorder state changed " + previous.ToString() + " -> " + current.ToString());
413 StateChanged?.Invoke(this, new RecorderStateChangedEventArgs(previous, current, byPolicy));
415 RecorderErrorFactory.ThrowIfError(Native.SetStateChangedCallback(_handle, _stateChangedCallback, IntPtr.Zero),
416 "Setting state changed callback failed");
419 private void RegisterRecordingProgressCallback()
421 _recordingProgressCallback = (ulong elapsedTime, ulong fileSize, IntPtr userData) =>
423 RecordingProgress?.Invoke(this, new RecordingProgressEventArgs(elapsedTime, fileSize));
425 RecorderErrorFactory.ThrowIfError(Native.SetRecordingProgressCallback(_handle, _recordingProgressCallback, IntPtr.Zero),
426 "Setting status changed callback failed");
429 private void RegisterAudioStreamDeliveredCallback()
431 _audioStreamCallback = (IntPtr stream, int streamSize, AudioSampleType type, int channel, uint recordingTime, IntPtr userData) =>
433 AudioStreamDelivered?.Invoke(this, new AudioStreamDeliveredEventArgs(stream, streamSize, type, channel, recordingTime));
435 RecorderErrorFactory.ThrowIfError(Native.SetAudioStreamCallback(_handle, _audioStreamCallback, IntPtr.Zero),
436 "Setting audiostream callback failed");
439 private void RegisterRecordingLimitReachedEvent()
441 _recordingLimitReachedCallback = (RecordingLimitType type, IntPtr userData) =>
443 RecordingLimitReached?.Invoke(this, new RecordingLimitReachedEventArgs(type));
445 RecorderErrorFactory.ThrowIfError(Native.SetLimitReachedCallback(_handle, _recordingLimitReachedCallback, IntPtr.Zero),
446 "Setting limit reached callback failed");
449 private void RegisterMuxedStreamEvent()
451 _muxedStreamCallback = (IntPtr stream, int streamSize, ulong offset, IntPtr userData) =>
453 MuxedStreamDelivered?.Invoke(this, new MuxedStreamDeliveredEventArgs(stream, streamSize, offset));
455 RecorderErrorFactory.ThrowIfError(Native.SetMuxedStreamCallback(_handle, _muxedStreamCallback, IntPtr.Zero),
456 "Setting muxed stream callback failed");
458 #endregion Callback registrations