- Add the license boilerplate to media tool files
[platform/core/csapi/tizenfx.git] / src / Tizen.Multimedia / MediaTool / MediaPacket.cs
1 /*
2  * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 using System;
18 using System.Diagnostics;
19 using System.Runtime.InteropServices;
20 using System.Threading;
21 using Tizen.Internals.Errors;
22
23 namespace Tizen.Multimedia
24 {
25     /// <summary>
26     /// Represents a packet for multimedia.
27     /// </summary>
28     public abstract class MediaPacket : IDisposable
29     {
30         private IntPtr _handle = IntPtr.Zero;
31
32         /// <summary>
33         /// Initializes a new instance of the MediaPacket class with the specified media format.
34         /// </summary>
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)
42         {
43             if (format == null)
44             {
45                 throw new ArgumentNullException(nameof(format));
46             }
47
48             if (format.Type == MediaFormatType.Container)
49             {
50                 throw new ArgumentException("Container format can't be used to create a new packet.");
51             }
52
53             Initialize(format);
54             _format = format;
55         }
56
57         /// <summary>
58         /// Initializes a new instance of the MediaPacket class from a native handle.
59         /// </summary>
60         /// <param name="handle">The native handle to be used.</param>
61         internal MediaPacket(IntPtr handle)
62         {
63             _handle = handle;
64
65             IntPtr formatHandle = IntPtr.Zero;
66             int ret = Interop.MediaPacket.GetFormat(handle, out formatHandle);
67
68             MediaToolDebug.AssertNoError(ret);
69
70             try
71             {
72                 if (formatHandle != IntPtr.Zero)
73                 {
74                     _format = MediaFormat.FromHandle(formatHandle);
75                 }
76             }
77             finally
78             {
79                 Interop.MediaFormat.Unref(formatHandle);
80             }
81         }
82
83         ~MediaPacket()
84         {
85             Dispose(false);
86         }
87
88         /// <summary>
89         /// Creates and initializes a native handle for the current object.
90         /// </summary>
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)
95         {
96             IntPtr formatHandle = IntPtr.Zero;
97
98             if (format.Type == MediaFormatType.Container)
99             {
100                 throw new ArgumentException("Creating a packet for container is not supported.");
101             }
102
103             try
104             {
105                 formatHandle = format.AsNativeHandle();
106
107                 int ret = Interop.MediaPacket.Create(formatHandle, IntPtr.Zero, IntPtr.Zero, out _handle);
108                 MediaToolDebug.AssertNoError(ret);
109
110                 Debug.Assert(_handle != IntPtr.Zero, "Created handle must not be null");
111
112                 Alloc();
113             }
114             catch (Exception)
115             {
116                 if (_handle != IntPtr.Zero)
117                 {
118                     Interop.MediaPacket.Destroy(_handle);
119                     _handle = IntPtr.Zero;
120                 }
121
122                 throw;
123             }
124             finally
125             {
126                 if (formatHandle != IntPtr.Zero)
127                 {
128                     Interop.MediaFormat.Unref(formatHandle);
129                 }
130             }
131         }
132
133         /// <summary>
134         /// Allocates internal buffer.
135         /// </summary>
136         /// <exception cref="NotEnoughMemoryException">Out of memory.</exception>
137         /// <exception cref="System.InvalidOperationException">Operation failed.</exception>
138         private void Alloc()
139         {
140             ErrorCode ret = (ErrorCode)Interop.MediaPacket.Alloc(_handle);
141             if (ret == ErrorCode.None)
142             {
143                 return;
144             }
145
146             switch (ret)
147             {
148                 case ErrorCode.OutOfMemory:
149                     throw new NotEnoughMemoryException("Failed to allocate buffer for the packet.");
150
151                 default:
152                     throw new InvalidOperationException("Failed to create a packet.");
153             }
154
155         }
156
157         private readonly MediaFormat _format;
158
159         /// <summary>
160         /// Gets the media format of the current packet.
161         /// </summary>
162         public MediaFormat Format
163         {
164             get
165             {
166                 ValidateNotDisposed();
167                 return _format;
168             }
169         }
170
171         /// <summary>
172         /// Gets or sets the PTS(Presentation Time Stamp) value of the current packet.
173         /// </summary>
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>
177         public ulong Pts
178         {
179             get
180             {
181                 ValidateNotDisposed();
182
183                 ulong value = 0;
184                 int ret = Interop.MediaPacket.GetPts(_handle, out value);
185
186                 MediaToolDebug.AssertNoError(ret);
187
188                 return value;
189             }
190             set
191             {
192                 ValidateNotDisposed();
193                 ValidateNotLocked();
194
195                 int ret = Interop.MediaPacket.SetPts(_handle, value);
196
197                 MediaToolDebug.AssertNoError(ret);
198             }
199         }
200
201         /// <summary>
202         /// Gets or sets the DTS(Decoding Time Stamp) value of the current packet.
203         /// </summary>
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>
207         public ulong Dts
208         {
209             get
210             {
211                 ValidateNotDisposed();
212
213                 ulong value = 0;
214                 int ret = Interop.MediaPacket.GetDts(_handle, out value);
215
216                 MediaToolDebug.AssertNoError(ret);
217
218                 return value;
219             }
220             set
221             {
222                 ValidateNotDisposed();
223                 ValidateNotLocked();
224
225                 int ret = Interop.MediaPacket.SetDts(_handle, value);
226
227                 MediaToolDebug.AssertNoError(ret);
228             }
229         }
230
231         /// <summary>
232         /// Gets a value indicating whether the packet is encoded type.
233         /// </summary>
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
237         {
238             get
239             {
240                 ValidateNotDisposed();
241
242                 bool value = false;
243                 int ret = Interop.MediaPacket.IsEncoded(_handle, out value);
244
245                 MediaToolDebug.AssertNoError(ret);
246
247                 return value;
248             }
249         }
250
251         private MediaPacketBuffer _buffer;
252
253         /// <summary>
254         /// Gets the buffer of the packet.
255         /// </summary>
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
262         {
263             get
264             {
265                 ValidateNotDisposed();
266
267                 if (IsVideoPlaneSupported)
268                 {
269                     return null;
270                 }
271
272                 if (_buffer == null)
273                 {
274                     _buffer = GetBuffer();
275                 }
276
277                 return _buffer;
278             }
279         }
280
281         /// <summary>
282         /// Gets or sets a length of data written in the <see cref="Buffer"/>.
283         /// </summary>
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.
291         ///     </exception>
292         public int BufferWrittenLength
293         {
294             get
295             {
296                 ValidateNotDisposed();
297
298                 ulong value = 0;
299                 int ret = Interop.MediaPacket.GetBufferSize(_handle, out value);
300                 MediaToolDebug.AssertNoError(ret);
301
302                 Debug.Assert(value < int.MaxValue);
303
304                 return (int)value;
305             }
306             set
307             {
308                 ValidateNotDisposed();
309                 ValidateNotLocked();
310
311                 if (IsVideoPlaneSupported)
312                 {
313                     throw new InvalidOperationException(
314                         "This packet uses VideoPlanes instead of Buffer.");
315                 }
316
317                 Debug.Assert(Buffer != null);
318
319                 if (value < 0 || value >= Buffer.Length)
320                 {
321                     throw new ArgumentOutOfRangeException("value must be less than Buffer.Size.");
322                 }
323
324                 int ret = Interop.MediaPacket.SetBufferSize(_handle, (ulong)value);
325                 MediaToolDebug.AssertNoError(ret);
326             }
327         }
328
329         private MediaPacketVideoPlane[] _videoPlanes;
330
331         /// <summary>
332         /// Gets the video planes of the packet.
333         /// </summary>
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
340         {
341             get
342             {
343                 ValidateNotDisposed();
344
345                 if (!IsVideoPlaneSupported)
346                 {
347                     return null;
348                 }
349
350                 if (_videoPlanes == null)
351                 {
352                     _videoPlanes = GetVideoPlanes();
353                 }
354
355                 return _videoPlanes;
356             }
357         }
358
359         /// <summary>
360         /// Gets or sets the buffer flags of the packet.
361         /// </summary>
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.
365         ///     </exception>
366         public MediaPacketBufferFlags BufferFlags
367         {
368             get
369             {
370                 ValidateNotDisposed();
371
372                 int value = 0;
373
374                 int ret = Interop.MediaPacket.GetBufferFlags(_handle, out value);
375
376                 MediaToolDebug.AssertNoError(ret);
377
378                 return (MediaPacketBufferFlags)value;
379             }
380
381             set
382             {
383                 ValidateNotDisposed();
384                 ValidateNotLocked();
385
386                 int ret = Interop.MediaPacket.ResetBufferFlags(_handle);
387
388                 MediaToolDebug.AssertNoError(ret);
389
390                 ret = Interop.MediaPacket.SetBufferFlags(_handle, (int)value);
391
392                 MediaToolDebug.AssertNoError(ret);
393             }
394         }
395
396         /// <summary>
397         /// Gets a value indicating whether the packet has been disposed of.
398         /// </summary>
399         /// <value>true if the packet has been disposed of; otherwise, false.</value>
400         public bool IsDisposed
401         {
402             get
403             {
404                 return _isDisposed;
405             }
406         }
407
408         private bool _isDisposed = false;
409
410         /// <summary>
411         /// Releases all resources.
412         /// </summary>
413         /// <exception cref="InvalidOperationException">
414         ///     The MediaPacket can not be disposed which means it being used by another module.
415         ///     </exception>
416         public void Dispose()
417         {
418             if (_isDisposed)
419             {
420                 return;
421             }
422             ValidateNotLocked();
423
424             Dispose(true);
425             GC.SuppressFinalize(this);
426         }
427
428         protected virtual void Dispose(bool disposing)
429         {
430             if (_isDisposed)
431             {
432                 return;
433             }
434
435             if (_handle != IntPtr.Zero)
436             {
437                 Interop.MediaPacket.Destroy(_handle);
438                 _handle = IntPtr.Zero;
439             }
440
441             _isDisposed = true;
442         }
443
444         internal IntPtr GetHandle()
445         {
446             ValidateNotDisposed();
447
448             Debug.Assert(_handle != IntPtr.Zero, "The handle is invalid!");
449
450             return _handle;
451         }
452
453         /// <summary>
454         /// Validate the current object has not been disposed of.
455         /// </summary>
456         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed of.</exception>
457         private void ValidateNotDisposed()
458         {
459             if (_isDisposed)
460             {
461                 throw new ObjectDisposedException("This packet has already been disposed of.");
462             }
463         }
464
465         /// <summary>
466         /// Validate the current object is not locked.
467         /// </summary>
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()
471         {
472             ValidateNotDisposed();
473
474             if (_lock.IsLocked)
475             {
476                 throw new InvalidOperationException("Can't perform any writing operation." +
477                     "The packet is in use, internally.");
478             }
479         }
480         /// <summary>
481         /// Ensures whether the packet is writable.
482         /// </summary>
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()
486         {
487             ValidateNotDisposed();
488             ValidateNotLocked();
489         }
490
491         /// <summary>
492         /// Ensures whether the packet is readable.
493         /// </summary>
494         /// <exception cref="ObjectDisposedException">The MediaPacket already has been disposed of.</exception>
495         internal void EnsureReadableState()
496         {
497             ValidateNotDisposed();
498         }
499
500         /// <summary>
501         /// Gets a value indicating whether the packet is raw video format.
502         /// </summary>
503         /// <value>true if the packet is raw video format; otherwise, false.</value>
504         private bool IsVideoPlaneSupported
505         {
506             get
507             {
508                 return !IsEncoded && Format.Type == MediaFormatType.Video;
509             }
510         }
511
512         /// <summary>
513         /// Retrieves video planes of the current packet.
514         /// </summary>
515         /// <returns>The <see cref="MediaPacketVideoPlane"/>s allocated to the current MediaPacket.</returns>
516         private MediaPacketVideoPlane[] GetVideoPlanes()
517         {
518             Debug.Assert(_handle != IntPtr.Zero, "The handle is invalid!");
519
520             uint numberOfPlanes = 0;
521             int ret = Interop.MediaPacket.GetNumberOfVideoPlanes(_handle, out numberOfPlanes);
522
523             MediaToolDebug.AssertNoError(ret);
524
525             MediaPacketVideoPlane[] planes = new MediaPacketVideoPlane[numberOfPlanes];
526
527             for (int i = 0; i < numberOfPlanes; ++i)
528             {
529                 planes[i] = new MediaPacketVideoPlane(this, i);
530             }
531
532             return planes;
533         }
534
535         /// <summary>
536         /// Retrieves the buffer of the current packet.
537         /// </summary>
538         /// <returns>The <see cref="MediaPacketBuffer"/> allocated to the current MediaPacket.</returns>
539         private MediaPacketBuffer GetBuffer()
540         {
541             Debug.Assert(_handle != IntPtr.Zero, "The handle is invalid!");
542
543             IntPtr dataHandle = IntPtr.Zero;
544
545             int ret = Interop.MediaPacket.GetBufferData(_handle, out dataHandle);
546             MediaToolDebug.AssertNoError(ret);
547
548             Debug.Assert(dataHandle != IntPtr.Zero, "Data handle is invalid!");
549
550             int size = 0;
551             ret = Interop.MediaPacket.GetAllocatedBufferSize(_handle, out size);
552             MediaToolDebug.AssertNoError(ret);
553
554             return new MediaPacketBuffer(this, dataHandle, size);
555         }
556
557         #region Lock operations
558         private readonly LockState _lock = new LockState();
559
560         /// <summary>
561         /// Provides a thread-safe lock state controller.
562         /// </summary>
563         private sealed class LockState
564         {
565             const int LOCKED = 1;
566             const int UNLOCKED = 0;
567
568             private int _locked = UNLOCKED;
569
570             internal void SetLock()
571             {
572                 if (Interlocked.CompareExchange(ref _locked, LOCKED, UNLOCKED) == LOCKED)
573                 {
574                     throw new InvalidOperationException("The packet is already locked.");
575                 }
576             }
577
578             internal void SetUnlock()
579             {
580                 if (Interlocked.CompareExchange(ref _locked, UNLOCKED, LOCKED) == UNLOCKED)
581                 {
582                     Debug.Fail("The packet to unlock is not locked. " +
583                         "There must be an error somewhere that a lock isn't disposed correctly.");
584                 }
585             }
586
587             internal bool IsLocked
588             {
589                 get
590                 {
591                     return Interlocked.CompareExchange(ref _locked, 0, 0) == LOCKED;
592                 }
593             }
594         }
595
596         /// <summary>
597         /// Provides a thread-safe lock controller.
598         /// </summary>
599         /// <example>
600         /// using (var lock = BaseMeadiPacket.Lock(mediaPacket))
601         /// {
602         ///     ....
603         /// }
604         /// </example>
605         internal sealed class Lock : IDisposable
606         {
607             private readonly MediaPacket _packet;
608             private readonly GCHandle _gcHandle;
609
610             internal Lock(MediaPacket packet)
611             {
612                 Debug.Assert(packet != null, "The packet is null!");
613
614                 packet.ValidateNotDisposed();
615
616                 _packet = packet;
617
618                 _packet._lock.SetLock();
619
620                 _gcHandle = GCHandle.Alloc(this);
621
622                 SetExtra(GCHandle.ToIntPtr(_gcHandle));
623             }
624
625             internal static Lock FromHandle(IntPtr handle)
626             {
627                 Debug.Assert(handle != IntPtr.Zero);
628
629                 IntPtr extra = GetExtra(handle);
630
631                 Debug.Assert(extra != IntPtr.Zero, "Extra of packet must not be null.");
632
633                 return (Lock)GCHandle.FromIntPtr(extra).Target;
634             }
635
636             private void SetExtra(IntPtr ptr)
637             {
638                 int ret = Interop.MediaPacket.SetExtra(_packet._handle, ptr);
639
640                 MediaToolDebug.AssertNoError(ret);
641             }
642
643             private static IntPtr GetExtra(IntPtr handle)
644             {
645                 IntPtr value = IntPtr.Zero;
646
647                 int ret = Interop.MediaPacket.GetExtra(handle, out value);
648
649                 MediaToolDebug.AssertNoError(ret);
650
651                 return value;
652             }
653
654             internal IntPtr GetHandle()
655             {
656                 return _packet.GetHandle();
657             }
658
659             internal MediaPacket MediaPacket
660             {
661                 get
662                 {
663                     return _packet;
664                 }
665             }
666
667             private bool _isDisposed = false;
668
669             public void Dispose()
670             {
671                 if (!_isDisposed)
672                 {
673                     // TODO rollback after the corresponding native api is fixed.
674                     //SetExtra(IntPtr.Zero);
675
676                     if (_gcHandle.IsAllocated)
677                     {
678                         _gcHandle.Free();
679                     }
680
681                     //We can assure that at this point '_packet' is always locked by this lock.
682                     _packet._lock.SetUnlock();
683
684                     _isDisposed = true;
685                 }
686             }
687         }
688         #endregion
689
690         /// <summary>
691         /// Creates an object of the MediaPacekt with the specified <see cref="MediaFormat"/>.
692         /// </summary>
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)
696         {
697             return new SimpleMediaPacket(format);
698         }
699
700         internal static MediaPacket From(IntPtr handle)
701         {
702             return new SimpleMediaPacket(handle);
703         }
704     }
705
706     internal class SimpleMediaPacket : MediaPacket
707     {
708         internal SimpleMediaPacket(MediaFormat format) : base(format)
709         {
710         }
711
712         internal SimpleMediaPacket(IntPtr handle) : base(handle)
713         {
714         }
715     }
716 }