/// <summary> /// Initialize a new instance of the <see cref="StreamRecorder"/> class. /// </summary> /// <exception cref="NotSupportedException">The feature is not supported.</exception> /// <since_tizen> 3 </since_tizen> /// <feature> http://tizen.org/feature/multimedia.stream_recorder </feature> public StreamRecorder() { if (IsSupported() == false) { throw new NotSupportedException( $"The feature({Feature}) is not supported on the current device."); } try { Native.Create(out _handle).ThrowIfError("Failed to create stream recorder."); } catch (TypeLoadException) { throw new NotSupportedException("StreamRecorder is not supported."); } LoadCapabilities(); RegisterStreamRecorderNotifiedEvent(); RegisterBufferConsumedEvent(); RegisterRecordingStatusChangedEvent(); RegisterRecordingErrorOccurredEvent(); RegisterRecordingLimitReachedEvent(); }
private void RegisterRecordingStatusChangedEvent() { _recordingStatusCallback = (elapsedTime, fileSize, _) => { RecordingStatusChanged?.Invoke(this, new RecordingStatusChangedEventArgs((long)elapsedTime, (long)fileSize)); }; Native.SetStatusChangedCallback(_handle, _recordingStatusCallback). ThrowIfError("Failed to initialize status changed event."); }
private void RegisterRecordingLimitReachedEvent() { _recordingLimitReachedCallback = (type, _) => { RecordingLimitReached?.Invoke(this, new RecordingLimitReachedEventArgs(type)); }; Native.SetLimitReachedCallback(_handle, _recordingLimitReachedCallback). ThrowIfError("Failed to initialize limit reached event."); }
private void RegisterRecordingErrorOccurredEvent() { _recorderErrorCallback = (error, currentState, _) => { ErrorOccurred?.Invoke(this, new StreamRecorderErrorOccurredEventArgs( error == StreamRecorderErrorCode.OutOfStorage ? StreamRecorderError.OutOfStorage : StreamRecorderError.InternalError, currentState)); }; Native.SetErrorCallback(_handle, _recorderErrorCallback). ThrowIfError("Failed to set error callback"); }
/// <summary> /// Pauses recording. /// </summary> /// <remarks> /// Recording can be resumed with <see cref="Start"/>.<br/> /// <br/> /// The recorder state must be <see cref="RecorderState.Recording"/> state by <see cref="Start"/>.<br/> /// <br/> /// It has no effect if the recorder is already in the <see cref="RecorderState.Paused"/> state. /// </remarks> /// <exception cref="InvalidOperationException">The recorder is not in the valid state.</exception> /// <exception cref="ObjectDisposedException">The <see cref="StreamRecorder"/> has already been disposed.</exception> /// <seealso cref="Start"/> /// <seealso cref="Commit"/> /// <seealso cref="Cancel"/> /// <since_tizen> 3 </since_tizen> public void Pause() { if (State == RecorderState.Paused) { return; } ValidateState(RecorderState.Recording); Native.Pause(Handle).ThrowIfError("Failed to pause the stream recorder."); }
/// <summary> /// Starts recording. /// </summary> /// <remarks> /// The recorder state must be <see cref="RecorderState.Ready"/> state by /// <see cref="Prepare(StreamRecorderOptions)"/> or /// <see cref="RecorderState.Paused"/> state by <see cref="Pause"/>.<br/> /// <br/> /// It has no effect if the recorder is already in the <see cref="RecorderState.Recording"/> state. /// </remarks> /// <exception cref="InvalidOperationException">The recorder is not in the valid state.</exception> /// <exception cref="UnauthorizedAccessException">The access of the resources can not be granted.</exception> /// <exception cref="ObjectDisposedException">The <see cref="StreamRecorder"/> has already been disposed.</exception> /// <seealso cref="Pause"/> /// <seealso cref="Commit"/> /// <seealso cref="Cancel"/> /// <since_tizen> 3 </since_tizen> public void Start() { if (State == RecorderState.Recording) { return; } ValidateState(RecorderState.Ready, RecorderState.Paused); Native.Start(Handle).ThrowIfError("Failed to start the stream recorder."); }
/// <summary> /// Unprepares the stream recorder. /// </summary> /// <remarks> /// The recorder state must be <see cref="RecorderState.Ready"/> state by /// <see cref="Prepare(StreamRecorderOptions)"/>, <see cref="Cancel"/> and <see cref="Commit"/>.<br/> /// The recorder state will be <see cref="RecorderState.Idle"/>.<br/> /// <br/> /// It has no effect if the recorder is already in the <see cref="RecorderState.Idle"/> state. /// </remarks> /// <exception cref="InvalidOperationException">The recorder is not in the valid state.</exception> /// <exception cref="ObjectDisposedException">The <see cref="StreamRecorder"/> has already been disposed.</exception> /// <seealso cref="Prepare"/> /// <since_tizen> 3 </since_tizen> public void Unprepare() { if (State == RecorderState.Idle) { return; } ValidateState(RecorderState.Ready); Native.Unprepare(Handle).ThrowIfError("Failed to reset the stream recorder."); }
private static IEnumerable <RecorderAudioCodec> LoadAudioCodecs(StreamRecorder recorder) { var result = new List <RecorderAudioCodec>(); Native.AudioEncoders(recorder.Handle, (codec, _) => { result.Add(codec.ToRecorderEnum()); return(true); }).ThrowIfError("Failed to get the supported audio codecs."); return(result.AsReadOnly()); }
private static IEnumerable <RecorderFileFormat> LoadFileFormats(StreamRecorder recorder) { var result = new List <RecorderFileFormat>(); Native.FileFormats(recorder.Handle, (fileFormat, _) => { result.Add(fileFormat.ToRecorderEnum()); return(true); }).ThrowIfError("Failed to get the supported file formats."); return(result.AsReadOnly()); }
private static IEnumerable <RecorderVideoCodec> LoadVideoCodecs(StreamRecorder recorder) { var result = new List <RecorderVideoCodec>(); Native.VideoEncoderCallback callback = (codec, _) => { result.Add(codec.ToRecorderEnum()); return(true); }; Native.VideoEncoders(recorder.Handle, callback).ThrowIfError("Failed to get the supported video codecs."); return(result.AsReadOnly()); }
private static IEnumerable <Size> LoadResolutions(StreamRecorder recorder) { List <Size> result = new List <Size>(); Native.VideoResolutionCallback callback = (width, height, _) => { result.Add(new Size(width, height)); return(true); }; Native.VideoResolution(recorder.Handle, callback). ThrowIfError("Failed to get the supported video resolutions."); return(result.AsReadOnly()); }
private void RegisterStreamRecorderNotifiedEvent() { _notifiedCallback = (previous, current, notify, _) => { if (previous == 0) { return; } StateChanged?.Invoke(this, new StreamRecorderStateChangedEventArgs( (RecorderState)previous, (RecorderState)current)); }; Native.SetNotifiedCallback(_handle, _notifiedCallback). ThrowIfError("Failed to initialize state changed event."); }
/// <summary> /// Prepares the stream recorder with the specified options. /// </summary> /// <remarks>The recorder must be <see cref="RecorderState.Idle"/>.</remarks> /// <param name="options">The options for recording.</param> /// <exception cref="InvalidOperationException">The recorder is not in the valid state.</exception> /// <exception cref="ArgumentException">Both <see cref="StreamRecorderOptions.Audio"/> and /// <see cref="StreamRecorderOptions.Video"/> are null. /// </exception> /// <exception cref="NotSupportedException"><paramref name="options"/> contains a value which is not supported.</exception> /// <exception cref="ObjectDisposedException">The <see cref="StreamRecorder"/> has already been disposed.</exception> /// <seealso cref="Unprepare"/> /// <seealso cref="Start"/> /// <seealso cref="StreamRecorderOptions"/> /// <seealso cref="StreamRecorderAudioOptions"/> /// <seealso cref="StreamRecorderVideoOptions"/> /// <since_tizen> 4 </since_tizen> public void Prepare(StreamRecorderOptions options) { if (options == null) { throw new ArgumentNullException(nameof(options)); } ValidateState(RecorderState.Idle); options.Apply(this); Native.Prepare(Handle).ThrowIfError("Failed to prepare stream recorder."); _audioEnabled = options.Audio != null; _videoEnabled = options.Video != null; if (options.Video != null) { _sourceFormat = options.Video.SourceFormat; } }
private void RegisterBufferConsumedEvent() { _bufferConsumedCallback = (lockedPacketHandle, _) => { MediaPacket packet = null; // Lock must be disposed here, note that the packet won't be disposed. using (MediaPacket.Lock packetLock = MediaPacket.Lock.FromHandle(lockedPacketHandle)) { Debug.Assert(packetLock != null); packet = packetLock.MediaPacket; } BufferConsumed?.Invoke(this, new StreamRecorderBufferConsumedEventArgs(packet)); }; Native.SetBufferConsumedCallback(_handle, _bufferConsumedCallback). ThrowIfError("Failed to initialize buffer consumed event."); }
/// <summary> /// Pushes a packet as recording raw data. /// </summary> /// <param name="packet">An audio or video packet to record.</param> /// <remarks> /// The recorder state must be <see cref="RecorderState.Recording"/> state by <see cref="Start"/>. /// </remarks> /// <exception cref="InvalidOperationException"> /// The recorder is not in the valid state.<br/> /// -or-<br/> /// <paramref name="packet"/> is an audio packet but audio recording is not enabled(See <see cref="StreamRecorderOptions.Audio"/>).<br/> /// -or-<br/> /// <paramref name="packet"/> is a video packet but video recording is not enabled(See <see cref="StreamRecorderOptions.Video"/>).<br/> /// -or-<br/> /// <paramref name="packet"/> is a video packet but the <see cref="VideoMediaFormat.MimeType"/> does not match the video source format.<br/> /// -or-<br/> /// An internal error occurs. /// </exception> /// <exception cref="ObjectDisposedException">The <see cref="StreamRecorder"/> has already been disposed.</exception> /// <see cref="Prepare(StreamRecorderOptions)"/> /// <seealso cref="StreamRecorderOptions.Audio"/> /// <seealso cref="StreamRecorderOptions.Video"/> /// <seealso cref="StreamRecorderVideoOptions.SourceFormat"/> /// <since_tizen> 3 </since_tizen> public void PushBuffer(MediaPacket packet) { if (packet == null) { throw new ArgumentNullException(nameof(packet)); } ValidateState(RecorderState.Recording); switch (packet.Format.Type) { case MediaFormatType.Audio: if (_audioEnabled == false) { throw new InvalidOperationException("Audio option is not set."); } break; case MediaFormatType.Video: if (_videoEnabled == false) { throw new InvalidOperationException("Video option is not set."); } if (AreVideoTypesMatched(_sourceFormat, (packet.Format as VideoMediaFormat).MimeType) == false) { throw new InvalidOperationException("Video format does not match."); } break; default: throw new ArgumentException("Packet is not valid."); } Native.PushStreamBuffer(Handle, MediaPacket.Lock.Get(packet).GetHandle()) .ThrowIfError("Failed to push buffer."); }
/// <summary> /// Stops recording and saves the result. /// </summary> /// <remarks> /// The recorder state must be <see cref="RecorderState.Recording"/> state by <see cref="Start"/> or /// <see cref="RecorderState.Paused"/> state by <see cref="Pause"/>.<br/> /// <br/> /// The recorder state will be <see cref="RecorderState.Ready"/> after commit. /// <para> /// http://tizen.org/privilege/mediastorage is needed if the save path are relevant to media storage. /// http://tizen.org/privilege/externalstorage is needed if the save path are relevant to external storage. /// </para> /// </remarks> /// <privilege>http://tizen.org/privilege/mediastorage</privilege> /// <privilege>http://tizen.org/privilege/externalstorage</privilege> /// <exception cref="InvalidOperationException">The recorder is not in the valid state.</exception> /// <exception cref="UnauthorizedAccessException">The access to the resources can not be granted.</exception> /// <exception cref="ObjectDisposedException">The <see cref="StreamRecorder"/> has already been disposed.</exception> /// <seealso cref="Start"/> /// <seealso cref="Pause"/> /// <since_tizen> 3 </since_tizen> public void Commit() { ValidateState(RecorderState.Paused, RecorderState.Recording); Native.Commit(Handle).ThrowIfError("Failed to commit."); }
/// <summary> /// Cancels recording. /// The recording data is discarded and not written. /// </summary> /// <remarks> /// The recorder state must be <see cref="RecorderState.Recording"/> state by <see cref="Start"/> or /// <see cref="RecorderState.Paused"/> state by <see cref="Pause"/>. /// </remarks> /// <exception cref="InvalidOperationException">The recorder is not in the valid state.</exception> /// <exception cref="ObjectDisposedException">The <see cref="StreamRecorder"/> has already been disposed.</exception> /// <seealso cref="Start"/> /// <seealso cref="Pause"/> /// <since_tizen> 3 </since_tizen> public void Cancel() { ValidateState(RecorderState.Paused, RecorderState.Recording); Native.Cancel(Handle).ThrowIfError("Failed to cancel recording."); }