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;
19 using System.Runtime.InteropServices;
20 using System.Threading;
21 using Tizen.Internals.Errors;
23 namespace Tizen.Multimedia
26 /// Represents a packet for multimedia.
28 public abstract class MediaPacket : IDisposable
30 private IntPtr _handle = IntPtr.Zero;
33 /// Initializes a new instance of the MediaPacket class with the specified media format.
35 /// <param name="format">The media format containing properties for the packet.</param>
36 /// <exception cref="System.ArgumentNullException">format is null.</exception>
37 /// <exception cref="System.ArgumentException">
38 /// <see cref="MediaFormatType"/> of the specified format is <see cref="MediaFormatType.Container"/>.</exception>
39 /// <exception cref="NotEnoughMemoryException">Out of memory.</exception>
40 /// <exception cref="System.InvalidOperationException">Operation failed.</exception>
41 internal MediaPacket(MediaFormat format)
45 throw new ArgumentNullException(nameof(format));
48 if (format.Type == MediaFormatType.Container)
50 throw new ArgumentException("Container format can't be used to create a new packet.");
58 /// Initializes a new instance of the MediaPacket class from a native handle.
60 /// <param name="handle">The native handle to be used.</param>
61 internal MediaPacket(IntPtr handle)
65 IntPtr formatHandle = IntPtr.Zero;
66 int ret = Interop.MediaPacket.GetFormat(handle, out formatHandle);
68 MediaToolDebug.AssertNoError(ret);
72 if (formatHandle != IntPtr.Zero)
74 _format = MediaFormat.FromHandle(formatHandle);
79 Interop.MediaFormat.Unref(formatHandle);
89 /// Creates and initializes a native handle for the current object.
91 /// <param name="format">The format to be set to the media format.</param>
92 /// <exception cref="NotEnoughMemoryException">Out of memory.</exception>
93 /// <exception cref="System.InvalidOperationException">Operation failed.</exception>
94 private void Initialize(MediaFormat format)
96 IntPtr formatHandle = IntPtr.Zero;
98 if (format.Type == MediaFormatType.Container)
100 throw new ArgumentException("Creating a packet for container is not supported.");
105 formatHandle = format.AsNativeHandle();
107 int ret = Interop.MediaPacket.Create(formatHandle, IntPtr.Zero, IntPtr.Zero, out _handle);
108 MediaToolDebug.AssertNoError(ret);
110 Debug.Assert(_handle != IntPtr.Zero, "Created handle must not be null");
116 if (_handle != IntPtr.Zero)
118 Interop.MediaPacket.Destroy(_handle);
119 _handle = IntPtr.Zero;
126 if (formatHandle != IntPtr.Zero)
128 Interop.MediaFormat.Unref(formatHandle);
134 /// Allocates internal buffer.
136 /// <exception cref="NotEnoughMemoryException">Out of memory.</exception>
137 /// <exception cref="System.InvalidOperationException">Operation failed.</exception>
140 ErrorCode ret = (ErrorCode)Interop.MediaPacket.Alloc(_handle);
141 if (ret == ErrorCode.None)
148 case ErrorCode.OutOfMemory:
149 throw new NotEnoughMemoryException("Failed to allocate buffer for the packet.");
152 throw new InvalidOperationException("Failed to create a packet.");
157 private readonly MediaFormat _format;
160 /// Gets the media format of the current packet.
162 public MediaFormat Format
166 ValidateNotDisposed();
172 /// Gets or sets the PTS(Presentation Time Stamp) value of the current packet.
174 /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed.</exception>
175 /// <exception cref="InvalidOperationException">
176 /// The MediaPacket is not writable state which means it being used by another module.</exception>
181 ValidateNotDisposed();
184 int ret = Interop.MediaPacket.GetPts(_handle, out value);
186 MediaToolDebug.AssertNoError(ret);
192 ValidateNotDisposed();
195 int ret = Interop.MediaPacket.SetPts(_handle, value);
197 MediaToolDebug.AssertNoError(ret);
202 /// Gets or sets the DTS(Decoding Time Stamp) value of the current packet.
204 /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed.</exception>
205 /// <exception cref="InvalidOperationException">
206 /// The MediaPacket is not in writable state which means it being used by another module.</exception>
211 ValidateNotDisposed();
214 int ret = Interop.MediaPacket.GetDts(_handle, out value);
216 MediaToolDebug.AssertNoError(ret);
222 ValidateNotDisposed();
225 int ret = Interop.MediaPacket.SetDts(_handle, value);
227 MediaToolDebug.AssertNoError(ret);
232 /// Gets a value indicating whether the packet is encoded type.
234 /// <value>true if the packet is encoded type; otherwise, false.</value>
235 /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed.</exception>
236 public bool IsEncoded
240 ValidateNotDisposed();
243 int ret = Interop.MediaPacket.IsEncoded(_handle, out value);
245 MediaToolDebug.AssertNoError(ret);
251 private MediaPacketBuffer _buffer;
254 /// Gets the buffer of the packet.
256 /// <value>The <see cref="MediaPacketBuffer"/> allocated to the packet.
257 /// This property will return null if the packet is raw video format.</value>
258 /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed.</exception>
259 /// <seealso cref="IsEncoded"/>
260 /// <seealso cref="VideoPlanes"/>
261 public MediaPacketBuffer Buffer
265 ValidateNotDisposed();
267 if (IsVideoPlaneSupported)
274 _buffer = GetBuffer();
282 /// Gets or sets a length of data written in the <see cref="Buffer"/>.
284 /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed.</exception>
285 /// <exception cref="ArgumentOutOfRangeException">
286 /// The value specified for this property is less than zero or greater than <see cref="MediaPacketBuffer.Length"/>.</exception>
287 /// <exception cref="InvalidOperationException">
288 /// The MediaPacket has <see cref="VideoPlanes"/> instead of <see cref="Buffer"/>.
289 /// <para>-or-</para>
290 /// The MediaPacket is not in writable state which means it being used by another module.
292 public int BufferWrittenLength
296 ValidateNotDisposed();
299 int ret = Interop.MediaPacket.GetBufferSize(_handle, out value);
300 MediaToolDebug.AssertNoError(ret);
302 Debug.Assert(value < int.MaxValue);
308 ValidateNotDisposed();
311 if (IsVideoPlaneSupported)
313 throw new InvalidOperationException(
314 "This packet uses VideoPlanes instead of Buffer.");
317 Debug.Assert(Buffer != null);
319 if (value < 0 || value >= Buffer.Length)
321 throw new ArgumentOutOfRangeException("value must be less than Buffer.Size.");
324 int ret = Interop.MediaPacket.SetBufferSize(_handle, (ulong)value);
325 MediaToolDebug.AssertNoError(ret);
329 private MediaPacketVideoPlane[] _videoPlanes;
332 /// Gets the video planes of the packet.
334 /// <value>The <see cref="MediaPacketVideoPlane"/>s allocated to the packet.
335 /// This property will return null if the packet is not raw video format.</value>
336 /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed.</exception>
337 /// <seealso cref="IsEncoded"/>
338 /// <seealso cref="Buffer"/>
339 public MediaPacketVideoPlane[] VideoPlanes
343 ValidateNotDisposed();
345 if (!IsVideoPlaneSupported)
350 if (_videoPlanes == null)
352 _videoPlanes = GetVideoPlanes();
360 /// Gets or sets the buffer flags of the packet.
362 /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed.</exception>
363 /// <exception cref="InvalidOperationException">
364 /// The MediaPacket is not in writable state which means it being used by another module.
366 public MediaPacketBufferFlags BufferFlags
370 ValidateNotDisposed();
374 int ret = Interop.MediaPacket.GetBufferFlags(_handle, out value);
376 MediaToolDebug.AssertNoError(ret);
378 return (MediaPacketBufferFlags)value;
383 ValidateNotDisposed();
386 int ret = Interop.MediaPacket.ResetBufferFlags(_handle);
388 MediaToolDebug.AssertNoError(ret);
390 ret = Interop.MediaPacket.SetBufferFlags(_handle, (int)value);
392 MediaToolDebug.AssertNoError(ret);
397 /// Gets a value indicating whether the packet has been disposed of.
399 /// <value>true if the packet has been disposed of; otherwise, false.</value>
400 public bool IsDisposed
408 private bool _isDisposed = false;
411 /// Releases all resources.
413 /// <exception cref="InvalidOperationException">
414 /// The MediaPacket can not be disposed which means it being used by another module.
416 public void Dispose()
425 GC.SuppressFinalize(this);
428 protected virtual void Dispose(bool disposing)
435 if (_handle != IntPtr.Zero)
437 Interop.MediaPacket.Destroy(_handle);
438 _handle = IntPtr.Zero;
444 internal IntPtr GetHandle()
446 ValidateNotDisposed();
448 Debug.Assert(_handle != IntPtr.Zero, "The handle is invalid!");
454 /// Validate the current object has not been disposed of.
456 /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed of.</exception>
457 private void ValidateNotDisposed()
461 throw new ObjectDisposedException("This packet has already been disposed of.");
466 /// Validate the current object is not locked.
468 /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed of.</exception>
469 /// <exception cref="InvalidOperationException">The MediaPacket is in use by another module.</exception>
470 private void ValidateNotLocked()
472 ValidateNotDisposed();
476 throw new InvalidOperationException("Can't perform any writing operation." +
477 "The packet is in use, internally.");
481 /// Ensures whether the packet is writable.
483 /// <exception cref="ObjectDisposedException">The MediaPacket already has been disposed of.</exception>
484 /// <exception cref="InvalidOperationException">The MediaPacket is being used by another module.</exception>
485 internal void EnsureWritableState()
487 ValidateNotDisposed();
492 /// Ensures whether the packet is readable.
494 /// <exception cref="ObjectDisposedException">The MediaPacket already has been disposed of.</exception>
495 internal void EnsureReadableState()
497 ValidateNotDisposed();
501 /// Gets a value indicating whether the packet is raw video format.
503 /// <value>true if the packet is raw video format; otherwise, false.</value>
504 private bool IsVideoPlaneSupported
508 return !IsEncoded && Format.Type == MediaFormatType.Video;
513 /// Retrieves video planes of the current packet.
515 /// <returns>The <see cref="MediaPacketVideoPlane"/>s allocated to the current MediaPacket.</returns>
516 private MediaPacketVideoPlane[] GetVideoPlanes()
518 Debug.Assert(_handle != IntPtr.Zero, "The handle is invalid!");
520 uint numberOfPlanes = 0;
521 int ret = Interop.MediaPacket.GetNumberOfVideoPlanes(_handle, out numberOfPlanes);
523 MediaToolDebug.AssertNoError(ret);
525 MediaPacketVideoPlane[] planes = new MediaPacketVideoPlane[numberOfPlanes];
527 for (int i = 0; i < numberOfPlanes; ++i)
529 planes[i] = new MediaPacketVideoPlane(this, i);
536 /// Retrieves the buffer of the current packet.
538 /// <returns>The <see cref="MediaPacketBuffer"/> allocated to the current MediaPacket.</returns>
539 private MediaPacketBuffer GetBuffer()
541 Debug.Assert(_handle != IntPtr.Zero, "The handle is invalid!");
543 IntPtr dataHandle = IntPtr.Zero;
545 int ret = Interop.MediaPacket.GetBufferData(_handle, out dataHandle);
546 MediaToolDebug.AssertNoError(ret);
548 Debug.Assert(dataHandle != IntPtr.Zero, "Data handle is invalid!");
551 ret = Interop.MediaPacket.GetAllocatedBufferSize(_handle, out size);
552 MediaToolDebug.AssertNoError(ret);
554 return new MediaPacketBuffer(this, dataHandle, size);
557 #region Lock operations
558 private readonly LockState _lock = new LockState();
561 /// Provides a thread-safe lock state controller.
563 private sealed class LockState
565 const int LOCKED = 1;
566 const int UNLOCKED = 0;
568 private int _locked = UNLOCKED;
570 internal void SetLock()
572 if (Interlocked.CompareExchange(ref _locked, LOCKED, UNLOCKED) == LOCKED)
574 throw new InvalidOperationException("The packet is already locked.");
578 internal void SetUnlock()
580 if (Interlocked.CompareExchange(ref _locked, UNLOCKED, LOCKED) == UNLOCKED)
582 Debug.Fail("The packet to unlock is not locked. " +
583 "There must be an error somewhere that a lock isn't disposed correctly.");
587 internal bool IsLocked
591 return Interlocked.CompareExchange(ref _locked, 0, 0) == LOCKED;
597 /// Provides a thread-safe lock controller.
600 /// using (var lock = BaseMeadiPacket.Lock(mediaPacket))
605 internal sealed class Lock : IDisposable
607 private readonly MediaPacket _packet;
608 private readonly GCHandle _gcHandle;
610 internal Lock(MediaPacket packet)
612 Debug.Assert(packet != null, "The packet is null!");
614 packet.ValidateNotDisposed();
618 _packet._lock.SetLock();
620 _gcHandle = GCHandle.Alloc(this);
622 SetExtra(GCHandle.ToIntPtr(_gcHandle));
625 internal static Lock FromHandle(IntPtr handle)
627 Debug.Assert(handle != IntPtr.Zero);
629 IntPtr extra = GetExtra(handle);
631 Debug.Assert(extra != IntPtr.Zero, "Extra of packet must not be null.");
633 return (Lock)GCHandle.FromIntPtr(extra).Target;
636 private void SetExtra(IntPtr ptr)
638 int ret = Interop.MediaPacket.SetExtra(_packet._handle, ptr);
640 MediaToolDebug.AssertNoError(ret);
643 private static IntPtr GetExtra(IntPtr handle)
645 IntPtr value = IntPtr.Zero;
647 int ret = Interop.MediaPacket.GetExtra(handle, out value);
649 MediaToolDebug.AssertNoError(ret);
654 internal IntPtr GetHandle()
656 return _packet.GetHandle();
659 internal MediaPacket MediaPacket
667 private bool _isDisposed = false;
669 public void Dispose()
673 // TODO rollback after the corresponding native api is fixed.
674 //SetExtra(IntPtr.Zero);
676 if (_gcHandle.IsAllocated)
681 //We can assure that at this point '_packet' is always locked by this lock.
682 _packet._lock.SetUnlock();
691 /// Creates an object of the MediaPacekt with the specified <see cref="MediaFormat"/>.
693 /// <param name="format">The media format for the new packet.</param>
694 /// <returns>A new MediaPacket object.</returns>
695 public static MediaPacket Create(MediaFormat format)
697 return new SimpleMediaPacket(format);
700 internal static MediaPacket From(IntPtr handle)
702 return new SimpleMediaPacket(handle);
706 internal class SimpleMediaPacket : MediaPacket
708 internal SimpleMediaPacket(MediaFormat format) : base(format)
712 internal SimpleMediaPacket(IntPtr handle) : base(handle)