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 /// <since_tizen> 3 </since_tizen>
43 public StreamRecorder()
47 Native.Create(out _handle).ThrowIfError("Failed to create stream recorder.");
49 catch (TypeLoadException)
51 throw new NotSupportedException("StreamRecorder is not supported.");
56 RegisterStreamRecorderNotifiedEvent();
57 RegisterBufferConsumedEvent();
58 RegisterRecordingStatusChangedEvent();
59 RegisterRecordingErrorOccurredEvent();
60 RegisterRecordingLimitReachedEvent();
63 internal NativeHandle Handle
69 throw new ObjectDisposedException(nameof(StreamRecorder));
77 /// Gets the current state of the stream recorder.
79 /// <exception cref="ObjectDisposedException">The <see cref="StreamRecorder"/> has already been disposed.</exception>
80 /// <since_tizen> 3 </since_tizen>
81 public RecorderState State
85 Native.GetState(Handle, out var val).ThrowIfError("Failed to get the stream recorder state.");
91 private void ValidateState(params RecorderState[] required)
93 Debug.Assert(required.Length > 0);
96 if (!required.Contains(curState))
98 throw new InvalidOperationException($"The stream recorder is not in a valid state. " +
99 $"Current State : { curState }, Valid State : { string.Join(", ", required) }.");
103 #region Operation methods
105 /// Prepares the stream recorder with the specified options.
107 /// <remarks>The recorder must be <see cref="RecorderState.Idle"/>.</remarks>
108 /// <param name="options">The options for recording.</param>
109 /// <exception cref="InvalidOperationException">The recorder is not in the valid state.</exception>
110 /// <exception cref="ArgumentException">Both <see cref="StreamRecorderOptions.Audio"/> and
111 /// <see cref="StreamRecorderOptions.Video"/> are null.
113 /// <exception cref="NotSupportedException"><paramref name="options"/> contains a value which is not supported.</exception>
114 /// <exception cref="ObjectDisposedException">The <see cref="StreamRecorder"/> has already been disposed.</exception>
115 /// <seealso cref="Unprepare"/>
116 /// <seealso cref="Start"/>
117 /// <seealso cref="StreamRecorderOptions"/>
118 /// <seealso cref="StreamRecorderAudioOptions"/>
119 /// <seealso cref="StreamRecorderVideoOptions"/>
120 /// <since_tizen> 4 </since_tizen>
121 public void Prepare(StreamRecorderOptions options)
125 throw new ArgumentNullException(nameof(options));
128 ValidateState(RecorderState.Idle);
132 Native.Prepare(Handle).ThrowIfError("Failed to prepare stream recorder.");
134 _audioEnabled = options.Audio != null;
135 _videoEnabled = options.Video != null;
137 if (options.Video != null)
139 _sourceFormat = options.Video.SourceFormat;
144 /// Unprepares the stream recorder.
147 /// The recorder state must be <see cref="RecorderState.Ready"/> state by
148 /// <see cref="Prepare(StreamRecorderOptions)"/>, <see cref="Cancel"/> and <see cref="Commit"/>.<br/>
149 /// The recorder state will be <see cref="RecorderState.Idle"/>.<br/>
151 /// It has no effect if the recorder is already in the <see cref="RecorderState.Idle"/> state.
153 /// <exception cref="InvalidOperationException">The recorder is not in the valid state.</exception>
154 /// <exception cref="ObjectDisposedException">The <see cref="StreamRecorder"/> has already been disposed.</exception>
155 /// <seealso cref="Prepare"/>
156 /// <since_tizen> 3 </since_tizen>
157 public void Unprepare()
159 if (State == RecorderState.Idle)
164 ValidateState(RecorderState.Ready);
166 Native.Unprepare(Handle).ThrowIfError("Failed to reset the stream recorder.");
170 /// Starts recording.
173 /// The recorder state must be <see cref="RecorderState.Ready"/> state by
174 /// <see cref="Prepare(StreamRecorderOptions)"/> or
175 /// <see cref="RecorderState.Paused"/> state by <see cref="Pause"/>.<br/>
177 /// It has no effect if the recorder is already in the <see cref="RecorderState.Recording"/> state.
179 /// <exception cref="InvalidOperationException">The recorder is not in the valid state.</exception>
180 /// <exception cref="UnauthorizedAccessException">The access of the resources can not be granted.</exception>
181 /// <exception cref="ObjectDisposedException">The <see cref="StreamRecorder"/> has already been disposed.</exception>
182 /// <seealso cref="Pause"/>
183 /// <seealso cref="Commit"/>
184 /// <seealso cref="Cancel"/>
185 /// <since_tizen> 3 </since_tizen>
188 if (State == RecorderState.Recording)
193 ValidateState(RecorderState.Ready, RecorderState.Paused);
195 Native.Start(Handle).ThrowIfError("Failed to start the stream recorder.");
199 /// Pauses recording.
202 /// Recording can be resumed with <see cref="Start"/>.<br/>
204 /// The recorder state must be <see cref="RecorderState.Recording"/> state by <see cref="Start"/>.<br/>
206 /// It has no effect if the recorder is already in the <see cref="RecorderState.Paused"/> state.
208 /// <exception cref="InvalidOperationException">The recorder is not in the valid state.</exception>
209 /// <exception cref="ObjectDisposedException">The <see cref="StreamRecorder"/> has already been disposed.</exception>
210 /// <seealso cref="Start"/>
211 /// <seealso cref="Commit"/>
212 /// <seealso cref="Cancel"/>
213 /// <since_tizen> 3 </since_tizen>
216 if (State == RecorderState.Paused)
221 ValidateState(RecorderState.Recording);
223 Native.Pause(Handle).ThrowIfError("Failed to pause the stream recorder.");
227 /// Stops recording and saves the result.
230 /// The recorder state must be <see cref="RecorderState.Recording"/> state by <see cref="Start"/> or
231 /// <see cref="RecorderState.Paused"/> state by <see cref="Pause"/>.<br/>
233 /// The recorder state will be <see cref="RecorderState.Ready"/> after commit.
235 /// http://tizen.org/privilege/mediastorage is needed if the save path are relevant to media storage.
236 /// http://tizen.org/privilege/externalstorage is needed if the save path are relevant to external storage.
239 /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
240 /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
241 /// <exception cref="InvalidOperationException">The recorder is not in the valid state.</exception>
242 /// <exception cref="UnauthorizedAccessException">The access to the resources can not be granted.</exception>
243 /// <exception cref="ObjectDisposedException">The <see cref="StreamRecorder"/> has already been disposed.</exception>
244 /// <seealso cref="Start"/>
245 /// <seealso cref="Pause"/>
246 /// <since_tizen> 3 </since_tizen>
249 ValidateState(RecorderState.Paused, RecorderState.Recording);
251 Native.Commit(Handle).ThrowIfError("Failed to commit.");
255 /// Cancels recording.
256 /// The recording data is discarded and not written.
259 /// The recorder state must be <see cref="RecorderState.Recording"/> state by <see cref="Start"/> or
260 /// <see cref="RecorderState.Paused"/> state by <see cref="Pause"/>.
262 /// <exception cref="InvalidOperationException">The recorder is not in the valid state.</exception>
263 /// <exception cref="ObjectDisposedException">The <see cref="StreamRecorder"/> has already been disposed.</exception>
264 /// <seealso cref="Start"/>
265 /// <seealso cref="Pause"/>
266 /// <since_tizen> 3 </since_tizen>
269 ValidateState(RecorderState.Paused, RecorderState.Recording);
271 Native.Cancel(Handle).ThrowIfError("Failed to cancel recording.");
274 private static bool AreVideoTypesMatched(StreamRecorderVideoFormat videoFormat, MediaFormatVideoMimeType mimeType)
276 return (videoFormat == StreamRecorderVideoFormat.Nv12 && mimeType == MediaFormatVideoMimeType.NV12) ||
277 (videoFormat == StreamRecorderVideoFormat.Nv21 && mimeType == MediaFormatVideoMimeType.NV21) ||
278 (videoFormat == StreamRecorderVideoFormat.I420 && mimeType == MediaFormatVideoMimeType.I420);
282 /// Pushes a packet as recording raw data.
284 /// <param name="packet">An audio or video packet to record.</param>
286 /// The recorder state must be <see cref="RecorderState.Recording"/> state by <see cref="Start"/>.
288 /// <exception cref="InvalidOperationException">
289 /// The recorder is not in the valid state.<br/>
291 /// <paramref name="packet"/> is an audio packet but audio recording is not enabled(See <see cref="StreamRecorderOptions.Audio"/>).<br/>
293 /// <paramref name="packet"/> is a video packet but video recording is not enabled(See <see cref="StreamRecorderOptions.Video"/>).<br/>
295 /// <paramref name="packet"/> is a video packet but the <see cref="VideoMediaFormat.MimeType"/> does not match the video source format.<br/>
297 /// An internal error occurs.
299 /// <exception cref="ObjectDisposedException">The <see cref="StreamRecorder"/> has already been disposed.</exception>
300 /// <see cref="Prepare(StreamRecorderOptions)"/>
301 /// <seealso cref="StreamRecorderOptions.Audio"/>
302 /// <seealso cref="StreamRecorderOptions.Video"/>
303 /// <seealso cref="StreamRecorderVideoOptions.SourceFormat"/>
304 /// <since_tizen> 3 </since_tizen>
305 public void PushBuffer(MediaPacket packet)
309 throw new ArgumentNullException(nameof(packet));
312 ValidateState(RecorderState.Recording);
314 switch (packet.Format.Type)
316 case MediaFormatType.Audio:
317 if (_audioEnabled == false)
319 throw new InvalidOperationException("Audio option is not set.");
323 case MediaFormatType.Video:
324 if (_videoEnabled == false)
326 throw new InvalidOperationException("Video option is not set.");
329 if (AreVideoTypesMatched(_sourceFormat, (packet.Format as VideoMediaFormat).MimeType) == false)
331 throw new InvalidOperationException("Video format does not match.");
337 throw new ArgumentException("Packet is not valid.");
340 Native.PushStreamBuffer(Handle, MediaPacket.Lock.Get(packet).GetHandle())
341 .ThrowIfError("Failed to push buffer.");
346 #region Dispose support
348 /// Release any unmanaged resources used by this object.
350 /// <since_tizen> 3 </since_tizen>
351 public void Dispose()
357 /// Releases the resources used by the StreamRecorder.
359 /// <param name="disposing">
360 /// true to release both managed and unmanaged resources; false to release only unmanaged resources.
362 /// <since_tizen> 3 </since_tizen>
363 protected virtual void Dispose(bool disposing)