/*
* 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 Tizen.Internals.Errors;
using Native = Tizen.Multimedia.Interop.MediaPacket;
using NativeFormat = Tizen.Multimedia.Interop.MediaFormat;
namespace Tizen.Multimedia
{
///
/// Represents a packet for multimedia.
///
public abstract partial class MediaPacket : IBufferOwner, IDisposable
{
private IntPtr _handle = IntPtr.Zero;
///
/// Initializes a new instance of the MediaPacket class with the specified media format.
///
/// The media format containing properties for the packet.
/// is null.
///
/// The of the specified format is .
///
/// Operation failed.
internal MediaPacket(MediaFormat format)
{
if (format == null)
{
throw new ArgumentNullException(nameof(format));
}
Initialize(format);
_format = format;
_buffer = new Lazy(GetBuffer);
}
///
/// Initializes a new instance of the MediaPacket class from a native handle.
///
/// The native handle to be used.
internal MediaPacket(IntPtr handle)
{
_handle = handle;
int ret = Native.GetFormat(handle, out IntPtr formatHandle);
MultimediaDebug.AssertNoError(ret);
try
{
if (formatHandle != IntPtr.Zero)
{
_format = MediaFormat.FromHandle(formatHandle);
}
}
finally
{
NativeFormat.Unref(formatHandle);
}
}
///
/// Finalizes an instance of the MediaPacket class.
///
~MediaPacket()
{
Dispose(false);
}
///
/// Creates and initializes a native handle for the current object.
///
/// The format to be set to the media format.
/// Operation failed.
private void Initialize(MediaFormat format)
{
if (format.Type == MediaFormatType.Container)
{
throw new ArgumentException("Container format can't be used to create a new packet.",
nameof(format));
}
IntPtr formatHandle = IntPtr.Zero;
try
{
formatHandle = format.AsNativeHandle();
int ret = Native.Create(formatHandle, IntPtr.Zero, IntPtr.Zero, out _handle);
MultimediaDebug.AssertNoError(ret);
Debug.Assert(_handle != IntPtr.Zero, "Created handle must not be null");
Alloc();
}
catch (Exception)
{
if (_handle != IntPtr.Zero)
{
Native.Destroy(_handle);
_handle = IntPtr.Zero;
}
throw;
}
finally
{
if (formatHandle != IntPtr.Zero)
{
NativeFormat.Unref(formatHandle);
}
}
}
///
/// Allocates internal buffer.
///
/// Operation failed.
private void Alloc()
{
ErrorCode ret = (ErrorCode)Native.Alloc(_handle);
if (ret == ErrorCode.None)
{
return;
}
_handle = IntPtr.Zero;
switch (ret)
{
case ErrorCode.OutOfMemory:
throw new OutOfMemoryException("Failed to allocate buffer for the packet.");
default:
throw new InvalidOperationException("Failed to create a packet.");
}
}
private readonly MediaFormat _format;
///
/// Gets the media format of the current packet.
///
/// 3
public MediaFormat Format
{
get
{
ValidateNotDisposed();
return _format;
}
}
///
/// Gets or sets the PTS(Presentation Time Stamp) value of the current packet.
///
/// The MediaPacket has already been disposed of.
///
/// The MediaPacket is not in the writable state, which means it is being used by another module.
///
/// 3
public ulong Pts
{
get
{
ValidateNotDisposed();
int ret = Native.GetPts(_handle, out var value);
MultimediaDebug.AssertNoError(ret);
return value;
}
set
{
ValidateNotDisposed();
ValidateNotLocked();
int ret = Native.SetPts(_handle, value);
MultimediaDebug.AssertNoError(ret);
}
}
///
/// Gets or sets the DTS(Decoding Time Stamp) value of the current packet.
///
/// The MediaPacket has already been disposed of.
///
/// The MediaPacket is not in the writable state, which means it is being used by another module.
///
/// 3
public ulong Dts
{
get
{
ValidateNotDisposed();
int ret = Native.GetDts(_handle, out var value);
MultimediaDebug.AssertNoError(ret);
return value;
}
set
{
ValidateNotDisposed();
ValidateNotLocked();
int ret = Native.SetDts(_handle, value);
MultimediaDebug.AssertNoError(ret);
}
}
///
/// Gets or sets the duration value of the current packet.
///
/// The MediaPacket has already been disposed of.
///
/// The MediaPacket is not in the writable state, which means it is being used by another module.
///
/// 6
public ulong Duration
{
get
{
ValidateNotDisposed();
int ret = Native.GetDuration(_handle, out var value);
MultimediaDebug.AssertNoError(ret);
return value;
}
set
{
ValidateNotDisposed();
ValidateNotLocked();
int ret = Native.SetDuration(_handle, value);
MultimediaDebug.AssertNoError(ret);
}
}
///
/// Gets a value indicating whether the packet is the encoded type.
///
/// true if the packet is the encoded type; otherwise, false.
/// The MediaPacket has already been disposed of.
/// 3
public bool IsEncoded
{
get
{
ValidateNotDisposed();
int ret = Native.IsEncoded(_handle, out var value);
MultimediaDebug.AssertNoError(ret);
return value;
}
}
///
/// Gets or sets the rotation value of the current packet.
///
/// The specified value to set is invalid.
/// The MediaPacket has already been disposed of.
///
/// The MediaPacket is not in the writable state, which means it is being used by another module.
///
/// 5
public Rotation Rotation
{
get
{
ValidateNotDisposed();
int ret = Native.GetRotation(_handle, out var value);
MultimediaDebug.AssertNoError(ret);
var rotation = value < RotationFlip.HorizontalFlip ? (Rotation)value : Rotation.Rotate0;
return rotation;
}
set
{
ValidateNotDisposed();
ValidateNotLocked();
ValidationUtil.ValidateEnum(typeof(Rotation), value, nameof(value));
int ret = Native.SetRotation(_handle, (RotationFlip)value);
MultimediaDebug.AssertNoError(ret);
}
}
///
/// Gets or sets the flip value of the current packet.
///
///
/// will be ignored in set case. It's not supported in Native FW.
///
/// The specified value to set is invalid.
/// The MediaPacket has already been disposed of.
///
/// The MediaPacket is not in the writable state, which means it is being used by another module.
///
/// 5
public Flips Flip
{
get
{
ValidateNotDisposed();
int ret = Native.GetRotation(_handle, out var value);
MultimediaDebug.AssertNoError(ret);
var flip = (value < RotationFlip.HorizontalFlip) ? Flips.None :
(value == RotationFlip.HorizontalFlip ? Flips.Horizontal : Flips.Vertical);
return flip;
}
set
{
ValidateNotDisposed();
ValidateNotLocked();
ValidationUtil.ValidateEnum(typeof(Flips), value, nameof(value));
if (value == Flips.None)
{
return;
}
var flip = value == Flips.Horizontal ? RotationFlip.HorizontalFlip : RotationFlip.VerticalFlip;
int ret = Native.SetRotation(_handle, flip);
MultimediaDebug.AssertNoError(ret);
}
}
private Lazy _buffer;
///
/// Gets the buffer of the packet.
///
///
/// The allocated to the packet.
/// This property will return null if the packet is in the raw video format.
///
/// The MediaPacket has already been disposed of.
///
///
/// 3
public IMediaBuffer Buffer
{
get
{
ValidateNotDisposed();
if (IsVideoPlaneSupported)
{
return null;
}
return _buffer.Value;
}
}
///
/// Gets or sets a length of data written in the .
///
/// The MediaPacket has already been disposed of.
///
/// The value specified for this property is less than zero or greater than the length of the .
///
/// The MediaPacket has instead of .
/// -or-
/// The MediaPacket is not in the writable state, which means it is being used by another module.
///
/// 3
public int BufferWrittenLength
{
get
{
ValidateNotDisposed();
int ret = Native.GetBufferSize(_handle, out var value);
MultimediaDebug.AssertNoError(ret);
Debug.Assert(value < int.MaxValue);
return (int)value;
}
set
{
ValidateNotDisposed();
ValidateNotLocked();
if (IsVideoPlaneSupported)
{
throw new InvalidOperationException(
"This packet uses VideoPlanes instead of Buffer.");
}
Debug.Assert(Buffer != null);
if (value < 0 || value >= Buffer.Length)
{
throw new ArgumentOutOfRangeException(nameof(value), value,
"value must be less than Buffer.Size.");
}
int ret = Native.SetBufferSize(_handle, (ulong)value);
MultimediaDebug.AssertNoError(ret);
}
}
private MediaPacketVideoPlane[] _videoPlanes;
///
/// Gets the video planes of the packet.
///
/// The s allocated to the packet.
/// This property will return null if the packet is not in the raw video format.
/// The MediaPacket has already been disposed of.
///
///
/// 3
public MediaPacketVideoPlane[] VideoPlanes
{
get
{
ValidateNotDisposed();
if (!IsVideoPlaneSupported)
{
return null;
}
if (_videoPlanes == null)
{
_videoPlanes = GetVideoPlanes();
}
return _videoPlanes;
}
}
///
/// Gets or sets the buffer flags of the packet.
///
/// The MediaPacket has already been disposed of.
///
/// The MediaPacket is not in the writable state, which means it is being used by another module.
///
/// 3
public MediaPacketBufferFlags BufferFlags
{
get
{
ValidateNotDisposed();
int ret = Native.GetBufferFlags(_handle, out var value);
MultimediaDebug.AssertNoError(ret);
return value;
}
set
{
ValidateNotDisposed();
ValidateNotLocked();
int ret = Native.ResetBufferFlags(_handle);
MultimediaDebug.AssertNoError(ret);
ret = Native.SetBufferFlags(_handle, (int)value);
MultimediaDebug.AssertNoError(ret);
}
}
#region Dispose support
///
/// Gets a value indicating whether the packet has been disposed of.
///
/// true if the packet has been disposed of; otherwise, false.
/// 3
public bool IsDisposed => _isDisposed;
private bool _isDisposed = false;
///
/// Releases all resources used by the object.
///
///
/// The MediaPacket can not be disposed, which means it is being used by another module.
///
/// 3
public void Dispose()
{
if (_isDisposed)
{
return;
}
ValidateNotLocked();
Dispose(true);
GC.SuppressFinalize(this);
}
///
/// Releases the resources used by the object.
///
///
/// true to release both managed and unmanaged resources; false to release only unmanaged resources.
///
/// 3
protected virtual void Dispose(bool disposing)
{
if (_isDisposed)
{
return;
}
if (_handle != IntPtr.Zero)
{
Native.Destroy(_handle);
_handle = IntPtr.Zero;
}
_isDisposed = true;
}
///
/// Validates the current object has not been disposed of.
///
/// The MediaPacket has already been disposed of.
private void ValidateNotDisposed()
{
if (_isDisposed)
{
throw new ObjectDisposedException("This packet has already been disposed of.");
}
}
#endregion
internal IntPtr GetHandle()
{
ValidateNotDisposed();
Debug.Assert(_handle != IntPtr.Zero, "The handle is invalid!");
return _handle;
}
///
/// Ensures whether the packet is writable.
///
/// The MediaPacket has already been disposed of.
/// The MediaPacket is being used by another module.
internal void EnsureWritableState()
{
ValidateNotDisposed();
ValidateNotLocked();
}
///
/// Ensures whether the packet is readable.
///
/// The MediaPacket has already been disposed of.
internal void EnsureReadableState()
{
ValidateNotDisposed();
}
///
/// Gets a value indicating whether the packet is in the raw video format.
///
/// true if the packet is in the raw video format; otherwise, false.
private bool IsVideoPlaneSupported => !IsEncoded && Format.Type == MediaFormatType.Video;
///
/// Retrieves video planes of the current packet.
///
/// The s allocated to the current MediaPacket.
private MediaPacketVideoPlane[] GetVideoPlanes()
{
Debug.Assert(_handle != IntPtr.Zero, "The handle is invalid!");
int ret = Native.GetNumberOfVideoPlanes(_handle, out var numberOfPlanes);
MultimediaDebug.AssertNoError(ret);
MediaPacketVideoPlane[] planes = new MediaPacketVideoPlane[numberOfPlanes];
for (int i = 0; i < numberOfPlanes; ++i)
{
planes[i] = new MediaPacketVideoPlane(this, i);
}
return planes;
}
///
/// Retrieves the buffer of the current packet.
///
/// The allocated to the current MediaPacket.
private IMediaBuffer GetBuffer()
{
Debug.Assert(!IsDisposed, "Packet is already disposed!");
Debug.Assert(_handle != IntPtr.Zero, "The handle is invalid!");
int ret = Native.GetBufferData(_handle, out var dataHandle);
MultimediaDebug.AssertNoError(ret);
Debug.Assert(dataHandle != IntPtr.Zero, "Data handle is invalid!");
ret = Native.GetAllocatedBufferSize(_handle, out var size);
MultimediaDebug.AssertNoError(ret);
Debug.Assert(size >= 0, "size must not be negative!");
return new DependentMediaBuffer(this, dataHandle, size);
}
///
/// Creates an object of the MediaPacket with the specified .
///
/// The media format for the new packet.
/// A new MediaPacket object.
/// 3
public static MediaPacket Create(MediaFormat format)
{
return new SimpleMediaPacket(format);
}
internal static MediaPacket From(IntPtr handle)
{
return new SimpleMediaPacket(handle);
}
bool IBufferOwner.IsDisposed => IsDisposed;
bool IBufferOwner.IsBufferAccessible(object buffer, MediaBufferAccessMode accessMode) => true;
}
internal class SimpleMediaPacket : MediaPacket
{
internal SimpleMediaPacket(MediaFormat format) : base(format)
{
}
internal SimpleMediaPacket(IntPtr handle) : base(handle)
{
}
}
}