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