/*
* Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the License);
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Diagnostics;
using System.Linq;
using NativeHandle = Interop.StreamRecorderHandle;
using Native = Interop.StreamRecorder;
namespace Tizen.Multimedia
{
///
/// Provides the ability to record user buffer from application.
///
///
[Obsolete("Deprecated in API10; Will be removed in API12")]
public partial class StreamRecorder : IDisposable
{
private NativeHandle _handle;
private bool _disposed = false;
private bool _audioEnabled;
private bool _videoEnabled;
private StreamRecorderVideoFormat _sourceFormat;
private const string Feature = "http://tizen.org/feature/multimedia.stream_recorder";
private static bool IsSupported()
{
return System.Information.TryGetValue(Feature, out bool isSupported) && isSupported;
}
///
/// Initialize a new instance of the class.
///
/// The feature is not supported.
/// 3
/// http://tizen.org/feature/multimedia.stream_recorder
[Obsolete("Deprecated in API10; Will be removed in API12")]
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();
}
internal NativeHandle Handle
{
get
{
if (_disposed)
{
throw new ObjectDisposedException(nameof(StreamRecorder));
}
return _handle;
}
}
///
/// Gets the current state of the stream recorder.
///
/// The has already been disposed.
/// 3
[Obsolete("Deprecated in API10; Will be removed in API12")]
public RecorderState State
{
get
{
Native.GetState(Handle, out var val).ThrowIfError("Failed to get the stream recorder state.");
return val;
}
}
private void ValidateState(params RecorderState[] required)
{
Debug.Assert(required.Length > 0);
var curState = State;
if (!required.Contains(curState))
{
throw new InvalidOperationException($"The stream recorder is not in a valid state. " +
$"Current State : { curState }, Valid State : { string.Join(", ", required) }.");
}
}
#region Operation methods
///
/// Prepares the stream recorder with the specified options.
///
/// The recorder must be .
/// The options for recording.
/// The recorder is not in the valid state.
/// Both and
/// are null.
///
/// contains a value which is not supported.
/// The has already been disposed.
///
///
///
///
///
/// 4
[Obsolete("Deprecated in API10; Will be removed in API12")]
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;
}
}
///
/// Unprepares the stream recorder.
///
///
/// The recorder state must be state by
/// , and .
/// The recorder state will be .
///
/// It has no effect if the recorder is already in the state.
///
/// The recorder is not in the valid state.
/// The has already been disposed.
///
/// 3
[Obsolete("Deprecated in API10; Will be removed in API12")]
public void Unprepare()
{
if (State == RecorderState.Idle)
{
return;
}
ValidateState(RecorderState.Ready);
Native.Unprepare(Handle).ThrowIfError("Failed to reset the stream recorder.");
}
///
/// Starts recording.
///
///
/// The recorder state must be state by
/// or
/// state by .
///
/// It has no effect if the recorder is already in the state.
///
/// The recorder is not in the valid state.
/// The access of the resources can not be granted.
/// The has already been disposed.
///
///
///
/// 3
[Obsolete("Deprecated in API10; Will be removed in API12")]
public void Start()
{
if (State == RecorderState.Recording)
{
return;
}
ValidateState(RecorderState.Ready, RecorderState.Paused);
Native.Start(Handle).ThrowIfError("Failed to start the stream recorder.");
}
///
/// Pauses recording.
///
///
/// Recording can be resumed with .
///
/// The recorder state must be state by .
///
/// It has no effect if the recorder is already in the state.
///
/// The recorder is not in the valid state.
/// The has already been disposed.
///
///
///
/// 3
[Obsolete("Deprecated in API10; Will be removed in API12")]
public void Pause()
{
if (State == RecorderState.Paused)
{
return;
}
ValidateState(RecorderState.Recording);
Native.Pause(Handle).ThrowIfError("Failed to pause the stream recorder.");
}
///
/// Stops recording and saves the result.
///
///
/// The recorder state must be state by or
/// state by .
///
/// The recorder state will be after commit.
///
/// 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.
///
///
/// http://tizen.org/privilege/mediastorage
/// http://tizen.org/privilege/externalstorage
/// The recorder is not in the valid state.
/// The access to the resources can not be granted.
/// The has already been disposed.
///
///
/// 3
[Obsolete("Deprecated in API10; Will be removed in API12")]
public void Commit()
{
ValidateState(RecorderState.Paused, RecorderState.Recording);
Native.Commit(Handle).ThrowIfError("Failed to commit.");
}
///
/// Cancels recording.
/// The recording data is discarded and not written.
///
///
/// The recorder state must be state by or
/// state by .
///
/// The recorder is not in the valid state.
/// The has already been disposed.
///
///
/// 3
[Obsolete("Deprecated in API10; Will be removed in API12")]
public void Cancel()
{
ValidateState(RecorderState.Paused, RecorderState.Recording);
Native.Cancel(Handle).ThrowIfError("Failed to cancel recording.");
}
private static bool AreVideoTypesMatched(StreamRecorderVideoFormat videoFormat, MediaFormatVideoMimeType mimeType)
{
return (videoFormat == StreamRecorderVideoFormat.Nv12 && mimeType == MediaFormatVideoMimeType.NV12) ||
(videoFormat == StreamRecorderVideoFormat.Nv21 && mimeType == MediaFormatVideoMimeType.NV21) ||
(videoFormat == StreamRecorderVideoFormat.I420 && mimeType == MediaFormatVideoMimeType.I420);
}
///
/// Pushes a packet as recording raw data.
///
/// An audio or video packet to record.
///
/// The recorder state must be state by .
///
///
/// The recorder is not in the valid state.
/// -or-
/// is an audio packet but audio recording is not enabled(See ).
/// -or-
/// is a video packet but video recording is not enabled(See ).
/// -or-
/// is a video packet but the does not match the video source format.
/// -or-
/// An internal error occurs.
///
/// The has already been disposed.
///
///
///
///
/// 3
[Obsolete("Deprecated in API10; Will be removed in API12")]
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.");
}
#endregion
#region Dispose support
///
/// Release any unmanaged resources used by this object.
///
/// 3
[Obsolete("Deprecated in API10; Will be removed in API12")]
public void Dispose()
{
Dispose(true);
}
///
/// Releases the resources used by the StreamRecorder.
///
///
/// true to release both managed and unmanaged resources; false to release only unmanaged resources.
///
/// 3
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
_handle?.Dispose();
_disposed = true;
}
}
#endregion
}
}