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="ArgumentNullException">format is null.</exception>
37 /// <exception cref="ArgumentException">
38 /// <see cref="MediaFormatType"/> of the specified format is <see cref="MediaFormatType.Container"/>.</exception>
39 /// <exception cref="InvalidOperationException">Operation failed.</exception>
40 internal MediaPacket(MediaFormat format)
44 throw new ArgumentNullException(nameof(format));
47 if (format.Type == MediaFormatType.Container)
49 throw new ArgumentException("Container format can't be used to create a new packet.");
57 /// Initializes a new instance of the MediaPacket class from a native handle.
59 /// <param name="handle">The native handle to be used.</param>
60 internal MediaPacket(IntPtr handle)
65 int ret = Interop.MediaPacket.GetFormat(handle, out formatHandle);
67 MultimediaDebug.AssertNoError(ret);
71 if (formatHandle != IntPtr.Zero)
73 _format = MediaFormat.FromHandle(formatHandle);
78 Interop.MediaFormat.Unref(formatHandle);
88 /// Creates and initializes a native handle for the current object.
90 /// <param name="format">The format to be set to the media format.</param>
91 /// <exception cref="InvalidOperationException">Operation failed.</exception>
92 private void Initialize(MediaFormat format)
94 if (format.Type == MediaFormatType.Container)
96 throw new ArgumentException("Creating a packet for container is not supported.");
99 IntPtr formatHandle = IntPtr.Zero;
103 formatHandle = format.AsNativeHandle();
105 int ret = Interop.MediaPacket.Create(formatHandle, IntPtr.Zero, IntPtr.Zero, out _handle);
106 MultimediaDebug.AssertNoError(ret);
108 Debug.Assert(_handle != IntPtr.Zero, "Created handle must not be null");
114 if (_handle != IntPtr.Zero)
116 Interop.MediaPacket.Destroy(_handle);
117 _handle = IntPtr.Zero;
124 if (formatHandle != IntPtr.Zero)
126 Interop.MediaFormat.Unref(formatHandle);
132 /// Allocates internal buffer.
134 /// <exception cref="InvalidOperationException">Operation failed.</exception>
137 ErrorCode ret = (ErrorCode)Interop.MediaPacket.Alloc(_handle);
138 if (ret == ErrorCode.None)
145 case ErrorCode.OutOfMemory:
146 throw new OutOfMemoryException("Failed to allocate buffer for the packet.");
149 throw new InvalidOperationException("Failed to create a packet.");
154 private readonly MediaFormat _format;
157 /// Gets the media format of the current packet.
159 public MediaFormat Format
163 ValidateNotDisposed();
169 /// Gets or sets the PTS(Presentation Time Stamp) value of the current packet.
171 /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed.</exception>
172 /// <exception cref="InvalidOperationException">
173 /// The MediaPacket is not writable state which means it being used by another module.</exception>
178 ValidateNotDisposed();
181 int ret = Interop.MediaPacket.GetPts(_handle, out value);
183 MultimediaDebug.AssertNoError(ret);
189 ValidateNotDisposed();
192 int ret = Interop.MediaPacket.SetPts(_handle, value);
194 MultimediaDebug.AssertNoError(ret);
199 /// Gets or sets the DTS(Decoding Time Stamp) value of the current packet.
201 /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed.</exception>
202 /// <exception cref="InvalidOperationException">
203 /// The MediaPacket is not in writable state which means it being used by another module.</exception>
208 ValidateNotDisposed();
211 int ret = Interop.MediaPacket.GetDts(_handle, out value);
213 MultimediaDebug.AssertNoError(ret);
219 ValidateNotDisposed();
222 int ret = Interop.MediaPacket.SetDts(_handle, value);
224 MultimediaDebug.AssertNoError(ret);
229 /// Gets a value indicating whether the packet is encoded type.
231 /// <value>true if the packet is encoded type; otherwise, false.</value>
232 /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed.</exception>
233 public bool IsEncoded
237 ValidateNotDisposed();
240 int ret = Interop.MediaPacket.IsEncoded(_handle, out value);
242 MultimediaDebug.AssertNoError(ret);
248 private MediaPacketBuffer _buffer;
251 /// Gets the buffer of the packet.
253 /// <value>The <see cref="MediaPacketBuffer"/> allocated to the packet.
254 /// This property will return null if the packet is raw video format.</value>
255 /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed.</exception>
256 /// <seealso cref="IsEncoded"/>
257 /// <seealso cref="VideoPlanes"/>
258 public MediaPacketBuffer Buffer
262 ValidateNotDisposed();
264 if (IsVideoPlaneSupported)
271 _buffer = GetBuffer();
279 /// Gets or sets a length of data written in the <see cref="Buffer"/>.
281 /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed.</exception>
282 /// <exception cref="ArgumentOutOfRangeException">
283 /// The value specified for this property is less than zero or greater than <see cref="MediaPacketBuffer.Length"/>.</exception>
284 /// <exception cref="InvalidOperationException">
285 /// The MediaPacket has <see cref="VideoPlanes"/> instead of <see cref="Buffer"/>.\n
287 /// The MediaPacket is not in writable state which means it being used by another module.
289 public int BufferWrittenLength
293 ValidateNotDisposed();
296 int ret = Interop.MediaPacket.GetBufferSize(_handle, out value);
297 MultimediaDebug.AssertNoError(ret);
299 Debug.Assert(value < int.MaxValue);
305 ValidateNotDisposed();
308 if (IsVideoPlaneSupported)
310 throw new InvalidOperationException(
311 "This packet uses VideoPlanes instead of Buffer.");
314 Debug.Assert(Buffer != null);
316 if (value < 0 || value >= Buffer.Length)
318 throw new ArgumentOutOfRangeException("value must be less than Buffer.Size.");
321 int ret = Interop.MediaPacket.SetBufferSize(_handle, (ulong)value);
322 MultimediaDebug.AssertNoError(ret);
326 private MediaPacketVideoPlane[] _videoPlanes;
329 /// Gets the video planes of the packet.
331 /// <value>The <see cref="MediaPacketVideoPlane"/>s allocated to the packet.
332 /// This property will return null if the packet is not raw video format.</value>
333 /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed.</exception>
334 /// <seealso cref="IsEncoded"/>
335 /// <seealso cref="Buffer"/>
336 public MediaPacketVideoPlane[] VideoPlanes
340 ValidateNotDisposed();
342 if (!IsVideoPlaneSupported)
347 if (_videoPlanes == null)
349 _videoPlanes = GetVideoPlanes();
357 /// Gets or sets the buffer flags of the packet.
359 /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed.</exception>
360 /// <exception cref="InvalidOperationException">
361 /// The MediaPacket is not in writable state which means it being used by another module.
363 public MediaPacketBufferFlags BufferFlags
367 ValidateNotDisposed();
371 int ret = Interop.MediaPacket.GetBufferFlags(_handle, out value);
373 MultimediaDebug.AssertNoError(ret);
375 return (MediaPacketBufferFlags)value;
380 ValidateNotDisposed();
383 int ret = Interop.MediaPacket.ResetBufferFlags(_handle);
385 MultimediaDebug.AssertNoError(ret);
387 ret = Interop.MediaPacket.SetBufferFlags(_handle, (int)value);
389 MultimediaDebug.AssertNoError(ret);
394 /// Gets a value indicating whether the packet has been disposed of.
396 /// <value>true if the packet has been disposed of; otherwise, false.</value>
397 public bool IsDisposed
405 private bool _isDisposed = false;
409 /// Releases all resources used by the <see cref="MediaPacket"/> object.
411 /// <exception cref="InvalidOperationException">
412 /// The MediaPacket can not be disposed which means it being used by another module.
414 public void Dispose()
423 GC.SuppressFinalize(this);
427 /// Releases the resources used by the <see cref="MediaPacket"/> object.
429 /// <param name="disposing">
430 /// true to release both managed and unmanaged resources; false to release only unmanaged resources.
432 protected virtual void Dispose(bool disposing)
439 if (_handle != IntPtr.Zero)
441 Interop.MediaPacket.Destroy(_handle);
442 _handle = IntPtr.Zero;
448 internal IntPtr GetHandle()
450 ValidateNotDisposed();
452 Debug.Assert(_handle != IntPtr.Zero, "The handle is invalid!");
458 /// Validate the current object has not been disposed of.
460 /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed of.</exception>
461 private void ValidateNotDisposed()
465 throw new ObjectDisposedException("This packet has already been disposed of.");
470 /// Validate the current object is not locked.
472 /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed of.</exception>
473 /// <exception cref="InvalidOperationException">The MediaPacket is in use by another module.</exception>
474 private void ValidateNotLocked()
476 ValidateNotDisposed();
480 throw new InvalidOperationException("Can't perform any writing operation." +
481 "The packet is in use, internally.");
485 /// Ensures whether the packet is writable.
487 /// <exception cref="ObjectDisposedException">The MediaPacket already has been disposed of.</exception>
488 /// <exception cref="InvalidOperationException">The MediaPacket is being used by another module.</exception>
489 internal void EnsureWritableState()
491 ValidateNotDisposed();
496 /// Ensures whether the packet is readable.
498 /// <exception cref="ObjectDisposedException">The MediaPacket already has been disposed of.</exception>
499 internal void EnsureReadableState()
501 ValidateNotDisposed();
505 /// Gets a value indicating whether the packet is raw video format.
507 /// <value>true if the packet is raw video format; otherwise, false.</value>
508 private bool IsVideoPlaneSupported
512 return !IsEncoded && Format.Type == MediaFormatType.Video;
517 /// Retrieves video planes of the current packet.
519 /// <returns>The <see cref="MediaPacketVideoPlane"/>s allocated to the current MediaPacket.</returns>
520 private MediaPacketVideoPlane[] GetVideoPlanes()
522 Debug.Assert(_handle != IntPtr.Zero, "The handle is invalid!");
524 uint numberOfPlanes = 0;
525 int ret = Interop.MediaPacket.GetNumberOfVideoPlanes(_handle, out numberOfPlanes);
527 MultimediaDebug.AssertNoError(ret);
529 MediaPacketVideoPlane[] planes = new MediaPacketVideoPlane[numberOfPlanes];
531 for (int i = 0; i < numberOfPlanes; ++i)
533 planes[i] = new MediaPacketVideoPlane(this, i);
540 /// Retrieves the buffer of the current packet.
542 /// <returns>The <see cref="MediaPacketBuffer"/> allocated to the current MediaPacket.</returns>
543 private MediaPacketBuffer GetBuffer()
545 Debug.Assert(_handle != IntPtr.Zero, "The handle is invalid!");
549 int ret = Interop.MediaPacket.GetBufferData(_handle, out dataHandle);
550 MultimediaDebug.AssertNoError(ret);
552 Debug.Assert(dataHandle != IntPtr.Zero, "Data handle is invalid!");
555 ret = Interop.MediaPacket.GetAllocatedBufferSize(_handle, out size);
556 MultimediaDebug.AssertNoError(ret);
558 return new MediaPacketBuffer(this, dataHandle, size);
561 #region Lock operations
562 private readonly LockState _lock = new LockState();
565 /// Provides a thread-safe lock state controller.
567 private sealed class LockState
569 const int LOCKED = 1;
570 const int UNLOCKED = 0;
572 private int _locked = UNLOCKED;
574 internal void SetLock()
576 if (Interlocked.CompareExchange(ref _locked, LOCKED, UNLOCKED) == LOCKED)
578 throw new InvalidOperationException("The packet is already locked.");
582 internal void SetUnlock()
584 if (Interlocked.CompareExchange(ref _locked, UNLOCKED, LOCKED) == UNLOCKED)
586 Debug.Fail("The packet to unlock is not locked. " +
587 "There must be an error somewhere that a lock isn't disposed correctly.");
591 internal bool IsLocked
595 return Interlocked.CompareExchange(ref _locked, 0, 0) == LOCKED;
601 /// Provides a thread-safe lock controller.
604 /// using (var lock = BaseMediaPacket.Lock(mediaPacket))
609 internal sealed class Lock : IDisposable
611 private readonly MediaPacket _packet;
612 private readonly GCHandle _gcHandle;
613 private int _lockCount;
615 internal static Lock Get(MediaPacket packet)
617 Debug.Assert(packet != null);
621 Lock lck = FromHandle(packet._handle);
625 lck = new Lock(packet);
634 private Lock(MediaPacket packet)
636 Debug.Assert(packet != null, "The packet is null!");
638 packet.ValidateNotDisposed();
642 _packet._lock.SetLock();
644 _gcHandle = GCHandle.Alloc(this);
646 SetExtra(GCHandle.ToIntPtr(_gcHandle));
649 internal static Lock FromHandle(IntPtr handle)
651 Debug.Assert(handle != IntPtr.Zero);
653 IntPtr extra = GetExtra(handle);
655 if (extra == IntPtr.Zero)
660 return (Lock)GCHandle.FromIntPtr(extra).Target;
663 private void SetExtra(IntPtr ptr)
665 int ret = Interop.MediaPacket.SetExtra(_packet._handle, ptr);
667 MultimediaDebug.AssertNoError(ret);
670 private static IntPtr GetExtra(IntPtr handle)
674 int ret = Interop.MediaPacket.GetExtra(handle, out value);
676 MultimediaDebug.AssertNoError(ret);
681 internal IntPtr GetHandle()
683 return _packet.GetHandle();
686 internal MediaPacket MediaPacket
694 private bool _isDisposed = false;
696 public void Dispose()
706 SetExtra(IntPtr.Zero);
708 if (_gcHandle.IsAllocated)
713 //We can assure that at this point '_packet' is always locked by this lock.
714 _packet._lock.SetUnlock();
725 /// Creates an object of the MediaPacket with the specified <see cref="MediaFormat"/>.
727 /// <param name="format">The media format for the new packet.</param>
728 /// <returns>A new MediaPacket object.</returns>
729 public static MediaPacket Create(MediaFormat format)
731 return new SimpleMediaPacket(format);
734 internal static MediaPacket From(IntPtr handle)
736 return new SimpleMediaPacket(handle);
740 internal class SimpleMediaPacket : MediaPacket
742 internal SimpleMediaPacket(MediaFormat format) : base(format)
746 internal SimpleMediaPacket(IntPtr handle) : base(handle)