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="System.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="System.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="System.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;
408 /// Releases all resources.
410 /// <exception cref="InvalidOperationException">
411 /// The MediaPacket can not be disposed which means it being used by another module.
413 public void Dispose()
422 GC.SuppressFinalize(this);
425 protected virtual void Dispose(bool disposing)
432 if (_handle != IntPtr.Zero)
434 Interop.MediaPacket.Destroy(_handle);
435 _handle = IntPtr.Zero;
441 internal IntPtr GetHandle()
443 ValidateNotDisposed();
445 Debug.Assert(_handle != IntPtr.Zero, "The handle is invalid!");
451 /// Validate the current object has not been disposed of.
453 /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed of.</exception>
454 private void ValidateNotDisposed()
458 throw new ObjectDisposedException("This packet has already been disposed of.");
463 /// Validate the current object is not locked.
465 /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed of.</exception>
466 /// <exception cref="InvalidOperationException">The MediaPacket is in use by another module.</exception>
467 private void ValidateNotLocked()
469 ValidateNotDisposed();
473 throw new InvalidOperationException("Can't perform any writing operation." +
474 "The packet is in use, internally.");
478 /// Ensures whether the packet is writable.
480 /// <exception cref="ObjectDisposedException">The MediaPacket already has been disposed of.</exception>
481 /// <exception cref="InvalidOperationException">The MediaPacket is being used by another module.</exception>
482 internal void EnsureWritableState()
484 ValidateNotDisposed();
489 /// Ensures whether the packet is readable.
491 /// <exception cref="ObjectDisposedException">The MediaPacket already has been disposed of.</exception>
492 internal void EnsureReadableState()
494 ValidateNotDisposed();
498 /// Gets a value indicating whether the packet is raw video format.
500 /// <value>true if the packet is raw video format; otherwise, false.</value>
501 private bool IsVideoPlaneSupported
505 return !IsEncoded && Format.Type == MediaFormatType.Video;
510 /// Retrieves video planes of the current packet.
512 /// <returns>The <see cref="MediaPacketVideoPlane"/>s allocated to the current MediaPacket.</returns>
513 private MediaPacketVideoPlane[] GetVideoPlanes()
515 Debug.Assert(_handle != IntPtr.Zero, "The handle is invalid!");
517 uint numberOfPlanes = 0;
518 int ret = Interop.MediaPacket.GetNumberOfVideoPlanes(_handle, out numberOfPlanes);
520 MultimediaDebug.AssertNoError(ret);
522 MediaPacketVideoPlane[] planes = new MediaPacketVideoPlane[numberOfPlanes];
524 for (int i = 0; i < numberOfPlanes; ++i)
526 planes[i] = new MediaPacketVideoPlane(this, i);
533 /// Retrieves the buffer of the current packet.
535 /// <returns>The <see cref="MediaPacketBuffer"/> allocated to the current MediaPacket.</returns>
536 private MediaPacketBuffer GetBuffer()
538 Debug.Assert(_handle != IntPtr.Zero, "The handle is invalid!");
542 int ret = Interop.MediaPacket.GetBufferData(_handle, out dataHandle);
543 MultimediaDebug.AssertNoError(ret);
545 Debug.Assert(dataHandle != IntPtr.Zero, "Data handle is invalid!");
548 ret = Interop.MediaPacket.GetAllocatedBufferSize(_handle, out size);
549 MultimediaDebug.AssertNoError(ret);
551 return new MediaPacketBuffer(this, dataHandle, size);
554 #region Lock operations
555 private readonly LockState _lock = new LockState();
558 /// Provides a thread-safe lock state controller.
560 private sealed class LockState
562 const int LOCKED = 1;
563 const int UNLOCKED = 0;
565 private int _locked = UNLOCKED;
567 internal void SetLock()
569 if (Interlocked.CompareExchange(ref _locked, LOCKED, UNLOCKED) == LOCKED)
571 throw new InvalidOperationException("The packet is already locked.");
575 internal void SetUnlock()
577 if (Interlocked.CompareExchange(ref _locked, UNLOCKED, LOCKED) == UNLOCKED)
579 Debug.Fail("The packet to unlock is not locked. " +
580 "There must be an error somewhere that a lock isn't disposed correctly.");
584 internal bool IsLocked
588 return Interlocked.CompareExchange(ref _locked, 0, 0) == LOCKED;
594 /// Provides a thread-safe lock controller.
597 /// using (var lock = BaseMediaPacket.Lock(mediaPacket))
602 internal sealed class Lock : IDisposable
604 private readonly MediaPacket _packet;
605 private readonly GCHandle _gcHandle;
606 private int _lockCount;
608 internal static Lock Get(MediaPacket packet)
610 Debug.Assert(packet != null);
614 Lock lck = FromHandle(packet._handle);
618 lck = new Lock(packet);
627 private Lock(MediaPacket packet)
629 Debug.Assert(packet != null, "The packet is null!");
631 packet.ValidateNotDisposed();
635 _packet._lock.SetLock();
637 _gcHandle = GCHandle.Alloc(this);
639 SetExtra(GCHandle.ToIntPtr(_gcHandle));
642 internal static Lock FromHandle(IntPtr handle)
644 Debug.Assert(handle != IntPtr.Zero);
646 IntPtr extra = GetExtra(handle);
648 if (extra == IntPtr.Zero)
653 return (Lock)GCHandle.FromIntPtr(extra).Target;
656 private void SetExtra(IntPtr ptr)
658 int ret = Interop.MediaPacket.SetExtra(_packet._handle, ptr);
660 MultimediaDebug.AssertNoError(ret);
663 private static IntPtr GetExtra(IntPtr handle)
667 int ret = Interop.MediaPacket.GetExtra(handle, out value);
669 MultimediaDebug.AssertNoError(ret);
674 internal IntPtr GetHandle()
676 return _packet.GetHandle();
679 internal MediaPacket MediaPacket
687 private bool _isDisposed = false;
689 public void Dispose()
699 // TODO rollback after the corresponding native api is fixed.
700 //SetExtra(IntPtr.Zero);
702 if (_gcHandle.IsAllocated)
707 //We can assure that at this point '_packet' is always locked by this lock.
708 _packet._lock.SetUnlock();
719 /// Creates an object of the MediaPacket with the specified <see cref="MediaFormat"/>.
721 /// <param name="format">The media format for the new packet.</param>
722 /// <returns>A new MediaPacket object.</returns>
723 public static MediaPacket Create(MediaFormat format)
725 return new SimpleMediaPacket(format);
728 internal static MediaPacket From(IntPtr handle)
730 return new SimpleMediaPacket(handle);
734 internal class SimpleMediaPacket : MediaPacket
736 internal SimpleMediaPacket(MediaFormat format) : base(format)
740 internal SimpleMediaPacket(IntPtr handle) : base(handle)