Merge remote-tracking branch 'nui/tizen'
[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="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)
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="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="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
408         /// <summary>
409         /// Releases all resources used by the <see cref="MediaPacket"/> object.
410         /// </summary>
411         /// <exception cref="InvalidOperationException">
412         ///     The MediaPacket can not be disposed which means it being used by another module.
413         /// </exception>
414         public void Dispose()
415         {
416             if (_isDisposed)
417             {
418                 return;
419             }
420             ValidateNotLocked();
421
422             Dispose(true);
423             GC.SuppressFinalize(this);
424         }
425
426         /// <summary>
427         /// Releases the resources used by the <see cref="MediaPacket"/> object.
428         /// </summary>
429         /// <param name="disposing">
430         /// true to release both managed and unmanaged resources; false to release only unmanaged resources.
431         /// </param>
432         protected virtual void Dispose(bool disposing)
433         {
434             if (_isDisposed)
435             {
436                 return;
437             }
438
439             if (_handle != IntPtr.Zero)
440             {
441                 Interop.MediaPacket.Destroy(_handle);
442                 _handle = IntPtr.Zero;
443             }
444
445             _isDisposed = true;
446         }
447
448         internal IntPtr GetHandle()
449         {
450             ValidateNotDisposed();
451
452             Debug.Assert(_handle != IntPtr.Zero, "The handle is invalid!");
453
454             return _handle;
455         }
456
457         /// <summary>
458         /// Validate the current object has not been disposed of.
459         /// </summary>
460         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed of.</exception>
461         private void ValidateNotDisposed()
462         {
463             if (_isDisposed)
464             {
465                 throw new ObjectDisposedException("This packet has already been disposed of.");
466             }
467         }
468
469         /// <summary>
470         /// Validate the current object is not locked.
471         /// </summary>
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()
475         {
476             ValidateNotDisposed();
477
478             if (_lock.IsLocked)
479             {
480                 throw new InvalidOperationException("Can't perform any writing operation." +
481                     "The packet is in use, internally.");
482             }
483         }
484         /// <summary>
485         /// Ensures whether the packet is writable.
486         /// </summary>
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()
490         {
491             ValidateNotDisposed();
492             ValidateNotLocked();
493         }
494
495         /// <summary>
496         /// Ensures whether the packet is readable.
497         /// </summary>
498         /// <exception cref="ObjectDisposedException">The MediaPacket already has been disposed of.</exception>
499         internal void EnsureReadableState()
500         {
501             ValidateNotDisposed();
502         }
503
504         /// <summary>
505         /// Gets a value indicating whether the packet is raw video format.
506         /// </summary>
507         /// <value>true if the packet is raw video format; otherwise, false.</value>
508         private bool IsVideoPlaneSupported
509         {
510             get
511             {
512                 return !IsEncoded && Format.Type == MediaFormatType.Video;
513             }
514         }
515
516         /// <summary>
517         /// Retrieves video planes of the current packet.
518         /// </summary>
519         /// <returns>The <see cref="MediaPacketVideoPlane"/>s allocated to the current MediaPacket.</returns>
520         private MediaPacketVideoPlane[] GetVideoPlanes()
521         {
522             Debug.Assert(_handle != IntPtr.Zero, "The handle is invalid!");
523
524             uint numberOfPlanes = 0;
525             int ret = Interop.MediaPacket.GetNumberOfVideoPlanes(_handle, out numberOfPlanes);
526
527             MultimediaDebug.AssertNoError(ret);
528
529             MediaPacketVideoPlane[] planes = new MediaPacketVideoPlane[numberOfPlanes];
530
531             for (int i = 0; i < numberOfPlanes; ++i)
532             {
533                 planes[i] = new MediaPacketVideoPlane(this, i);
534             }
535
536             return planes;
537         }
538
539         /// <summary>
540         /// Retrieves the buffer of the current packet.
541         /// </summary>
542         /// <returns>The <see cref="MediaPacketBuffer"/> allocated to the current MediaPacket.</returns>
543         private MediaPacketBuffer GetBuffer()
544         {
545             Debug.Assert(_handle != IntPtr.Zero, "The handle is invalid!");
546
547             IntPtr dataHandle;
548
549             int ret = Interop.MediaPacket.GetBufferData(_handle, out dataHandle);
550             MultimediaDebug.AssertNoError(ret);
551
552             Debug.Assert(dataHandle != IntPtr.Zero, "Data handle is invalid!");
553
554             int size = 0;
555             ret = Interop.MediaPacket.GetAllocatedBufferSize(_handle, out size);
556             MultimediaDebug.AssertNoError(ret);
557
558             return new MediaPacketBuffer(this, dataHandle, size);
559         }
560
561         #region Lock operations
562         private readonly LockState _lock = new LockState();
563
564         /// <summary>
565         /// Provides a thread-safe lock state controller.
566         /// </summary>
567         private sealed class LockState
568         {
569             const int LOCKED = 1;
570             const int UNLOCKED = 0;
571
572             private int _locked = UNLOCKED;
573
574             internal void SetLock()
575             {
576                 if (Interlocked.CompareExchange(ref _locked, LOCKED, UNLOCKED) == LOCKED)
577                 {
578                     throw new InvalidOperationException("The packet is already locked.");
579                 }
580             }
581
582             internal void SetUnlock()
583             {
584                 if (Interlocked.CompareExchange(ref _locked, UNLOCKED, LOCKED) == UNLOCKED)
585                 {
586                     Debug.Fail("The packet to unlock is not locked. " +
587                         "There must be an error somewhere that a lock isn't disposed correctly.");
588                 }
589             }
590
591             internal bool IsLocked
592             {
593                 get
594                 {
595                     return Interlocked.CompareExchange(ref _locked, 0, 0) == LOCKED;
596                 }
597             }
598         }
599
600         /// <summary>
601         /// Provides a thread-safe lock controller.
602         /// </summary>
603         /// <example>
604         /// using (var lock = BaseMediaPacket.Lock(mediaPacket))
605         /// {
606         ///     ....
607         /// }
608         /// </example>
609         internal sealed class Lock : IDisposable
610         {
611             private readonly MediaPacket _packet;
612             private readonly GCHandle _gcHandle;
613             private int _lockCount;
614
615             internal static Lock Get(MediaPacket packet)
616             {
617                 Debug.Assert(packet != null);
618
619                 lock (packet)
620                 {
621                     Lock lck = FromHandle(packet._handle);
622
623                     if (lck == null)
624                     {
625                         lck = new Lock(packet);
626                     }
627
628                     lck._lockCount++;
629
630                     return lck;
631                 }
632             }
633
634             private Lock(MediaPacket packet)
635             {
636                 Debug.Assert(packet != null, "The packet is null!");
637
638                 packet.ValidateNotDisposed();
639
640                 _packet = packet;
641
642                 _packet._lock.SetLock();
643
644                 _gcHandle = GCHandle.Alloc(this);
645
646                 SetExtra(GCHandle.ToIntPtr(_gcHandle));
647             }
648
649             internal static Lock FromHandle(IntPtr handle)
650             {
651                 Debug.Assert(handle != IntPtr.Zero);
652
653                 IntPtr extra = GetExtra(handle);
654
655                 if (extra == IntPtr.Zero)
656                 {
657                     return null;
658                 }
659
660                 return (Lock)GCHandle.FromIntPtr(extra).Target;
661             }
662
663             private void SetExtra(IntPtr ptr)
664             {
665                 int ret = Interop.MediaPacket.SetExtra(_packet._handle, ptr);
666
667                 MultimediaDebug.AssertNoError(ret);
668             }
669
670             private static IntPtr GetExtra(IntPtr handle)
671             {
672                 IntPtr value;
673
674                 int ret = Interop.MediaPacket.GetExtra(handle, out value);
675
676                 MultimediaDebug.AssertNoError(ret);
677
678                 return value;
679             }
680
681             internal IntPtr GetHandle()
682             {
683                 return _packet.GetHandle();
684             }
685
686             internal MediaPacket MediaPacket
687             {
688                 get
689                 {
690                     return _packet;
691                 }
692             }
693
694             private bool _isDisposed = false;
695
696             public void Dispose()
697             {
698                 if (!_isDisposed)
699                 {
700                     lock (_packet)
701                     {
702                         _lockCount--;
703
704                         if (_lockCount == 0)
705                         {
706                             SetExtra(IntPtr.Zero);
707
708                             if (_gcHandle.IsAllocated)
709                             {
710                                 _gcHandle.Free();
711                             }
712
713                             //We can assure that at this point '_packet' is always locked by this lock.
714                             _packet._lock.SetUnlock();
715                         }
716                     }
717
718                     _isDisposed = true;
719                 }
720             }
721         }
722         #endregion
723
724         /// <summary>
725         /// Creates an object of the MediaPacket with the specified <see cref="MediaFormat"/>.
726         /// </summary>
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)
730         {
731             return new SimpleMediaPacket(format);
732         }
733
734         internal static MediaPacket From(IntPtr handle)
735         {
736             return new SimpleMediaPacket(handle);
737         }
738     }
739
740     internal class SimpleMediaPacket : MediaPacket
741     {
742         internal SimpleMediaPacket(MediaFormat format) : base(format)
743         {
744         }
745
746         internal SimpleMediaPacket(IntPtr handle) : base(handle)
747         {
748         }
749     }
750 }