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 NativeHandle = Interop.StreamRecorderHandle;
21 using Native = Interop.StreamRecorder;
23 namespace Tizen.Multimedia
26 /// Provides the ability to record user buffer from application.
28 /// <seealso cref="Recorder"/>
29 public partial class StreamRecorder : IDisposable
31 private NativeHandle _handle;
32 private bool _disposed = false;
34 private bool _audioEnabled;
35 private bool _videoEnabled;
36 private StreamRecorderVideoFormat _sourceFormat;
39 /// Initialize a new instance of the <see cref="StreamRecorder"/> class.
41 /// <exception cref="NotSupportedException">The feature is not supported.</exception>
42 public StreamRecorder()
46 Native.Create(out _handle).ThrowIfError("Failed to create stream recorder.");
48 catch (TypeLoadException)
50 throw new NotSupportedException("StreamRecorder is not supported.");
55 RegisterStreamRecorderNotifiedEvent();
56 RegisterBufferConsumedEvent();
57 RegisterRecordingStatusChangedEvent();
58 RegisterRecordingErrorOccurredEvent();
59 RegisterRecordingLimitReachedEvent();
62 internal NativeHandle Handle
68 throw new ObjectDisposedException(nameof(StreamRecorder));
76 /// Gets the current state of the stream recorder.
78 /// <exception cref="ObjectDisposedException">The <see cref="StreamRecorder"/> has already been disposed.</exception>
79 public RecorderState State
83 Native.GetState(Handle, out var val).ThrowIfError("Failed to get the stream recorder state.");
89 private void ValidateState(params RecorderState[] required)
91 Debug.Assert(required.Length > 0);
94 if (!required.Contains(curState))
96 throw new InvalidOperationException($"The stream recorder is not in a valid state. " +
97 $"Current State : { curState }, Valid State : { string.Join(", ", required) }.");
101 #region Operation methods
103 /// Prepares the stream recorder with the specified options.
105 /// <remarks>The recorder must be <see cref="RecorderState.Idle"/>.</remarks>
106 /// <param name="options">The options for recording.</param>
107 /// <exception cref="InvalidOperationException">The recorder is not in the valid state.</exception>
108 /// <exception cref="ArgumentException">Both <see cref="StreamRecorderOptions.Audio"/> and
109 /// <see cref="StreamRecorderOptions.Video"/> are null.
111 /// <exception cref="NotSupportedException"><paramref name="options"/> contains a value which is not supported.</exception>
112 /// <exception cref="ObjectDisposedException">The <see cref="StreamRecorder"/> has already been disposed.</exception>
113 /// <seealso cref="Unprepare"/>
114 /// <seealso cref="Start"/>
115 /// <seealso cref="StreamRecorderOptions"/>
116 /// <seealso cref="StreamRecorderAudioOptions"/>
117 /// <seealso cref="StreamRecorderVideoOptions"/>
118 public void Prepare(StreamRecorderOptions options)
122 throw new ArgumentNullException(nameof(options));
125 ValidateState(RecorderState.Idle);
129 Native.Prepare(Handle).ThrowIfError("Failed to prepare stream recorder.");
131 _audioEnabled = options.Audio != null;
132 _videoEnabled = options.Video != null;
134 if (options.Video != null)
136 _sourceFormat = options.Video.SourceFormat;
141 /// Unprepares the stream recorder.
144 /// The recorder state must be <see cref="RecorderState.Ready"/> state by
145 /// <see cref="Prepare(StreamRecorderOptions)"/>, <see cref="Cancel"/> and <see cref="Commit"/>.\n
146 /// The recorder state will be <see cref="RecorderState.Idle"/>.\n
148 /// It has no effect if the recorder is already in the <see cref="RecorderState.Idle"/> state.
150 /// <exception cref="InvalidOperationException">The recorder is not in the valid state.</exception>
151 /// <exception cref="ObjectDisposedException">The <see cref="StreamRecorder"/> has already been disposed.</exception>
152 /// <seealso cref="Prepare"/>
153 public void Unprepare()
155 if (State == RecorderState.Idle)
160 ValidateState(RecorderState.Ready);
162 Native.Unprepare(Handle).ThrowIfError("Failed to reset the stream recorder.");
166 /// Starts recording.
169 /// The recorder state must be <see cref="RecorderState.Ready"/> state by
170 /// <see cref="Prepare(StreamRecorderOptions)"/> or
171 /// <see cref="RecorderState.Paused"/> state by <see cref="Pause"/>.\n
173 /// It has no effect if the recorder is already in the <see cref="RecorderState.Recording"/> state.
175 /// <exception cref="InvalidOperationException">The recorder is not in the valid state.</exception>
176 /// <exception cref="UnauthorizedAccessException">The access of the resources can not be granted.</exception>
177 /// <exception cref="ObjectDisposedException">The <see cref="StreamRecorder"/> has already been disposed.</exception>
178 /// <seealso cref="Pause"/>
179 /// <seealso cref="Commit"/>
180 /// <seealso cref="Cancel"/>
183 if (State == RecorderState.Recording)
188 ValidateState(RecorderState.Ready, RecorderState.Paused);
190 Native.Start(Handle).ThrowIfError("Failed to start the stream recorder.");
194 /// Pauses recording.
197 /// Recording can be resumed with <see cref="Start"/>.\n
199 /// The recorder state must be <see cref="RecorderState.Recording"/> state by <see cref="Start"/> \n
201 /// It has no effect if the recorder is already in the <see cref="RecorderState.Paused"/> state.
203 /// <exception cref="InvalidOperationException">The recorder is not in the valid state.</exception>
204 /// <exception cref="ObjectDisposedException">The <see cref="StreamRecorder"/> has already been disposed.</exception>
205 /// <seealso cref="Start"/>
206 /// <seealso cref="Commit"/>
207 /// <seealso cref="Cancel"/>
210 if (State == RecorderState.Paused)
215 ValidateState(RecorderState.Recording);
217 Native.Pause(Handle).ThrowIfError("Failed to pause the stream recorder.");
221 /// Stops recording and saves the result.
224 /// The recorder state must be <see cref="RecorderState.Recording"/> state by <see cref="Start"/> or
225 /// <see cref="RecorderState.Paused"/> state by <see cref="Pause"/>.\n
227 /// The recorder state will be <see cref="RecorderState.Ready"/> after commit.\n
229 /// http://tizen.org/privilege/mediastorage is needed if the save path are relevant to media storage.
230 /// http://tizen.org/privilege/externalstorage is needed if the save path are relevant to external storage.
233 /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
234 /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
235 /// <exception cref="InvalidOperationException">The recorder is not in the valid state.</exception>
236 /// <exception cref="UnauthorizedAccessException">The access to the resources can not be granted.</exception>
237 /// <exception cref="ObjectDisposedException">The <see cref="StreamRecorder"/> has already been disposed.</exception>
238 /// <seealso cref="Start"/>
239 /// <seealso cref="Pause"/>
242 ValidateState(RecorderState.Paused, RecorderState.Recording);
244 Native.Commit(Handle).ThrowIfError("Failed to commit.");
248 /// Cancels recording.
249 /// The recording data is discarded and not written.
252 /// The recorder state must be <see cref="RecorderState.Recording"/> state by <see cref="Start"/> or
253 /// <see cref="RecorderState.Paused"/> state by <see cref="Pause"/>.
255 /// <exception cref="InvalidOperationException">The recorder is not in the valid state.</exception>
256 /// <exception cref="ObjectDisposedException">The <see cref="StreamRecorder"/> has already been disposed.</exception>
257 /// <seealso cref="Start"/>
258 /// <seealso cref="Pause"/>
261 ValidateState(RecorderState.Paused, RecorderState.Recording);
263 Native.Cancel(Handle).ThrowIfError("Failed to cancel recording.");
266 private static bool AreVideoTypesMatched(StreamRecorderVideoFormat videoFormat, MediaFormatVideoMimeType mimeType)
268 return (videoFormat == StreamRecorderVideoFormat.Nv12 && mimeType == MediaFormatVideoMimeType.NV12) ||
269 (videoFormat == StreamRecorderVideoFormat.Nv21 && mimeType == MediaFormatVideoMimeType.NV21) ||
270 (videoFormat == StreamRecorderVideoFormat.I420 && mimeType == MediaFormatVideoMimeType.I420);
274 /// Pushes a packet as recording raw data.
276 /// <param name="packet">An audio or video packet to record.</param>
278 /// The recorder state must be <see cref="RecorderState.Recording"/> state by <see cref="Start"/>.
280 /// <exception cref="InvalidOperationException">
281 /// The recorder is not in the valid state.\n
283 /// <paramref name="packet"/> is an audio packet but audio recording is not enabled(See <see cref="StreamRecorderOptions.Audio"/>).\n
285 /// <paramref name="packet"/> is a video packet but video recording is not enabled(See <see cref="StreamRecorderOptions.Video"/>).\n
287 /// <paramref name="packet"/> is a video packet but the <see cref="VideoMediaFormat.MimeType"/> does not match the video source format.\n
289 /// An internal error occurs.
291 /// <exception cref="ObjectDisposedException">The <see cref="StreamRecorder"/> has already been disposed.</exception>
292 /// <see cref="Prepare(StreamRecorderOptions)"/>
293 /// <seealso cref="StreamRecorderOptions.Audio"/>
294 /// <seealso cref="StreamRecorderOptions.Video"/>
295 /// <seealso cref="StreamRecorderVideoOptions.SourceFormat"/>
296 public void PushBuffer(MediaPacket packet)
300 throw new ArgumentNullException(nameof(packet));
303 ValidateState(RecorderState.Recording);
305 switch (packet.Format.Type)
307 case MediaFormatType.Audio:
308 if (_audioEnabled == false)
310 throw new InvalidOperationException("Audio option is not set.");
314 case MediaFormatType.Video:
315 if (_videoEnabled == false)
317 throw new InvalidOperationException("Video option is not set.");
320 if (AreVideoTypesMatched(_sourceFormat, (packet.Format as VideoMediaFormat).MimeType) == false)
322 throw new InvalidOperationException("Video format does not match.");
328 throw new ArgumentException("Packet is not valid.");
331 Native.PushStreamBuffer(Handle, MediaPacket.Lock.Get(packet).GetHandle())
332 .ThrowIfError("Failed to push buffer.");
337 #region Dispose support
339 /// Release any unmanaged resources used by this object.
341 public void Dispose()
347 /// Releases the resources used by the StreamRecorder.
349 /// <param name="disposing">
350 /// true to release both managed and unmanaged resources; false to release only unmanaged resources.
352 protected virtual void Dispose(bool disposing)