[Multimedia] Fixed a critical error of MediaPacket.
[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 partial 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"><paramref name="format"/> is null.</exception>
37         /// <exception cref="ArgumentException">
38         ///     The <see cref="MediaFormatType"/> of the specified format is <see cref="MediaFormatType.Container"/>.
39         /// </exception>
40         /// <exception cref="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;
66             int ret = Interop.MediaPacket.GetFormat(handle, out formatHandle);
67
68             MultimediaDebug.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="InvalidOperationException">Operation failed.</exception>
93         private void Initialize(MediaFormat format)
94         {
95             if (format.Type == MediaFormatType.Container)
96             {
97                 throw new ArgumentException("Creating a packet for container is not supported.");
98             }
99
100             IntPtr formatHandle = IntPtr.Zero;
101
102             try
103             {
104                 formatHandle = format.AsNativeHandle();
105
106                 int ret = Interop.MediaPacket.Create(formatHandle, IntPtr.Zero, IntPtr.Zero, out _handle);
107                 MultimediaDebug.AssertNoError(ret);
108
109                 Debug.Assert(_handle != IntPtr.Zero, "Created handle must not be null");
110
111                 Alloc();
112             }
113             catch (Exception)
114             {
115                 if (_handle != IntPtr.Zero)
116                 {
117                     Interop.MediaPacket.Destroy(_handle);
118                     _handle = IntPtr.Zero;
119                 }
120
121                 throw;
122             }
123             finally
124             {
125                 if (formatHandle != IntPtr.Zero)
126                 {
127                     Interop.MediaFormat.Unref(formatHandle);
128                 }
129             }
130         }
131
132         /// <summary>
133         /// Allocates internal buffer.
134         /// </summary>
135         /// <exception cref="InvalidOperationException">Operation failed.</exception>
136         private void Alloc()
137         {
138             ErrorCode ret = (ErrorCode)Interop.MediaPacket.Alloc(_handle);
139             if (ret == ErrorCode.None)
140             {
141                 return;
142             }
143
144             _handle = IntPtr.Zero;
145
146             switch (ret)
147             {
148                 case ErrorCode.OutOfMemory:
149                     throw new OutOfMemoryException("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 of.</exception>
175         /// <exception cref="InvalidOperationException">
176         ///     The MediaPacket is not in the writable state, which means it is being used by another module.
177         /// </exception>
178         public ulong Pts
179         {
180             get
181             {
182                 ValidateNotDisposed();
183
184                 ulong value = 0;
185                 int ret = Interop.MediaPacket.GetPts(_handle, out value);
186
187                 MultimediaDebug.AssertNoError(ret);
188
189                 return value;
190             }
191             set
192             {
193                 ValidateNotDisposed();
194                 ValidateNotLocked();
195
196                 int ret = Interop.MediaPacket.SetPts(_handle, value);
197
198                 MultimediaDebug.AssertNoError(ret);
199             }
200         }
201
202         /// <summary>
203         /// Gets or sets the DTS(Decoding Time Stamp) value of the current packet.
204         /// </summary>
205         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed of.</exception>
206         /// <exception cref="InvalidOperationException">
207         ///     The MediaPacket is not in the writable state, which means it is being used by another module.
208         /// </exception>
209         public ulong Dts
210         {
211             get
212             {
213                 ValidateNotDisposed();
214
215                 ulong value = 0;
216                 int ret = Interop.MediaPacket.GetDts(_handle, out value);
217
218                 MultimediaDebug.AssertNoError(ret);
219
220                 return value;
221             }
222             set
223             {
224                 ValidateNotDisposed();
225                 ValidateNotLocked();
226
227                 int ret = Interop.MediaPacket.SetDts(_handle, value);
228
229                 MultimediaDebug.AssertNoError(ret);
230             }
231         }
232
233         /// <summary>
234         /// Gets a value indicating whether the packet is the encoded type.
235         /// </summary>
236         /// <value>true if the packet is the encoded type; otherwise, false.</value>
237         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed of.</exception>
238         public bool IsEncoded
239         {
240             get
241             {
242                 ValidateNotDisposed();
243
244                 bool value = false;
245                 int ret = Interop.MediaPacket.IsEncoded(_handle, out value);
246
247                 MultimediaDebug.AssertNoError(ret);
248
249                 return value;
250             }
251         }
252
253         private MediaPacketBuffer _buffer;
254
255         /// <summary>
256         /// Gets the buffer of the packet.
257         /// </summary>
258         /// <value>The <see cref="MediaPacketBuffer"/> allocated to the packet.
259         ///     This property will return null if the packet is in the raw video format.</value>
260         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed of.</exception>
261         /// <seealso cref="IsEncoded"/>
262         /// <seealso cref="VideoPlanes"/>
263         public MediaPacketBuffer Buffer
264         {
265             get
266             {
267                 ValidateNotDisposed();
268
269                 if (IsVideoPlaneSupported)
270                 {
271                     return null;
272                 }
273
274                 if (_buffer == null)
275                 {
276                     _buffer = GetBuffer();
277                 }
278
279                 return _buffer;
280             }
281         }
282
283         /// <summary>
284         /// Gets or sets a length of data written in the <see cref="Buffer"/>.
285         /// </summary>
286         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed of.</exception>
287         /// <exception cref="ArgumentOutOfRangeException">
288         ///     The value specified for this property is less than zero or greater than <see cref="MediaPacketBuffer.Length"/>.</exception>
289         /// <exception cref="InvalidOperationException">
290         ///     The MediaPacket has <see cref="VideoPlanes"/> instead of <see cref="Buffer"/>.\n
291         ///     -or-\n
292         ///     The MediaPacket is not in the writable state, which means it is being used by another module.
293         ///     </exception>
294         public int BufferWrittenLength
295         {
296             get
297             {
298                 ValidateNotDisposed();
299
300                 ulong value = 0;
301                 int ret = Interop.MediaPacket.GetBufferSize(_handle, out value);
302                 MultimediaDebug.AssertNoError(ret);
303
304                 Debug.Assert(value < int.MaxValue);
305
306                 return (int)value;
307             }
308             set
309             {
310                 ValidateNotDisposed();
311                 ValidateNotLocked();
312
313                 if (IsVideoPlaneSupported)
314                 {
315                     throw new InvalidOperationException(
316                         "This packet uses VideoPlanes instead of Buffer.");
317                 }
318
319                 Debug.Assert(Buffer != null);
320
321                 if (value < 0 || value >= Buffer.Length)
322                 {
323                     throw new ArgumentOutOfRangeException("value must be less than Buffer.Size.");
324                 }
325
326                 int ret = Interop.MediaPacket.SetBufferSize(_handle, (ulong)value);
327                 MultimediaDebug.AssertNoError(ret);
328             }
329         }
330
331         private MediaPacketVideoPlane[] _videoPlanes;
332
333         /// <summary>
334         /// Gets the video planes of the packet.
335         /// </summary>
336         /// <value>The <see cref="MediaPacketVideoPlane"/>s allocated to the packet.
337         ///     This property will return null if the packet is not in the raw video format.</value>
338         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed of.</exception>
339         /// <seealso cref="IsEncoded"/>
340         /// <seealso cref="Buffer"/>
341         public MediaPacketVideoPlane[] VideoPlanes
342         {
343             get
344             {
345                 ValidateNotDisposed();
346
347                 if (!IsVideoPlaneSupported)
348                 {
349                     return null;
350                 }
351
352                 if (_videoPlanes == null)
353                 {
354                     _videoPlanes = GetVideoPlanes();
355                 }
356
357                 return _videoPlanes;
358             }
359         }
360
361         /// <summary>
362         /// Gets or sets the buffer flags of the packet.
363         /// </summary>
364         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed of.</exception>
365         /// <exception cref="InvalidOperationException">
366         ///     The MediaPacket is not in the writable state, which means it is being used by another module.
367         /// </exception>
368         public MediaPacketBufferFlags BufferFlags
369         {
370             get
371             {
372                 ValidateNotDisposed();
373
374                 int value = 0;
375
376                 int ret = Interop.MediaPacket.GetBufferFlags(_handle, out value);
377
378                 MultimediaDebug.AssertNoError(ret);
379
380                 return (MediaPacketBufferFlags)value;
381             }
382
383             set
384             {
385                 ValidateNotDisposed();
386                 ValidateNotLocked();
387
388                 int ret = Interop.MediaPacket.ResetBufferFlags(_handle);
389
390                 MultimediaDebug.AssertNoError(ret);
391
392                 ret = Interop.MediaPacket.SetBufferFlags(_handle, (int)value);
393
394                 MultimediaDebug.AssertNoError(ret);
395             }
396         }
397
398         /// <summary>
399         /// Gets a value indicating whether the packet has been disposed of.
400         /// </summary>
401         /// <value>true if the packet has been disposed of; otherwise, false.</value>
402         public bool IsDisposed
403         {
404             get
405             {
406                 return _isDisposed;
407             }
408         }
409
410         private bool _isDisposed = false;
411
412
413         /// <summary>
414         /// Releases all resources used by the <see cref="MediaPacket"/> object.
415         /// </summary>
416         /// <exception cref="InvalidOperationException">
417         ///     The MediaPacket can not be disposed, which means it is being used by another module.
418         /// </exception>
419         public void Dispose()
420         {
421             if (_isDisposed)
422             {
423                 return;
424             }
425             ValidateNotLocked();
426
427             Dispose(true);
428             GC.SuppressFinalize(this);
429         }
430
431         /// <summary>
432         /// Releases the resources used by the <see cref="MediaPacket"/> object.
433         /// </summary>
434         /// <param name="disposing">
435         /// true to release both managed and unmanaged resources; false to release only unmanaged resources.
436         /// </param>
437         protected virtual void Dispose(bool disposing)
438         {
439             if (_isDisposed)
440             {
441                 return;
442             }
443
444             if (_handle != IntPtr.Zero)
445             {
446                 Interop.MediaPacket.Destroy(_handle);
447                 _handle = IntPtr.Zero;
448             }
449
450             _isDisposed = true;
451         }
452
453         internal IntPtr GetHandle()
454         {
455             ValidateNotDisposed();
456
457             Debug.Assert(_handle != IntPtr.Zero, "The handle is invalid!");
458
459             return _handle;
460         }
461
462         /// <summary>
463         /// Validates the current object has not been disposed of.
464         /// </summary>
465         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed of.</exception>
466         private void ValidateNotDisposed()
467         {
468             if (_isDisposed)
469             {
470                 throw new ObjectDisposedException("This packet has already been disposed of.");
471             }
472         }
473
474         /// <summary>
475         /// Ensures whether the packet is writable.
476         /// </summary>
477         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed of.</exception>
478         /// <exception cref="InvalidOperationException">The MediaPacket is being used by another module.</exception>
479         internal void EnsureWritableState()
480         {
481             ValidateNotDisposed();
482             ValidateNotLocked();
483         }
484
485         /// <summary>
486         /// Ensures whether the packet is readable.
487         /// </summary>
488         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed of.</exception>
489         internal void EnsureReadableState()
490         {
491             ValidateNotDisposed();
492         }
493
494         /// <summary>
495         /// Gets a value indicating whether the packet is in the raw video format.
496         /// </summary>
497         /// <value>true if the packet is in the raw video format; otherwise, false.</value>
498         private bool IsVideoPlaneSupported
499         {
500             get
501             {
502                 return !IsEncoded && Format.Type == MediaFormatType.Video;
503             }
504         }
505
506         /// <summary>
507         /// Retrieves video planes of the current packet.
508         /// </summary>
509         /// <returns>The <see cref="MediaPacketVideoPlane"/>s allocated to the current MediaPacket.</returns>
510         private MediaPacketVideoPlane[] GetVideoPlanes()
511         {
512             Debug.Assert(_handle != IntPtr.Zero, "The handle is invalid!");
513
514             uint numberOfPlanes = 0;
515             int ret = Interop.MediaPacket.GetNumberOfVideoPlanes(_handle, out numberOfPlanes);
516
517             MultimediaDebug.AssertNoError(ret);
518
519             MediaPacketVideoPlane[] planes = new MediaPacketVideoPlane[numberOfPlanes];
520
521             for (int i = 0; i < numberOfPlanes; ++i)
522             {
523                 planes[i] = new MediaPacketVideoPlane(this, i);
524             }
525
526             return planes;
527         }
528
529         /// <summary>
530         /// Retrieves the buffer of the current packet.
531         /// </summary>
532         /// <returns>The <see cref="MediaPacketBuffer"/> allocated to the current MediaPacket.</returns>
533         private MediaPacketBuffer GetBuffer()
534         {
535             Debug.Assert(_handle != IntPtr.Zero, "The handle is invalid!");
536
537             IntPtr dataHandle;
538
539             int ret = Interop.MediaPacket.GetBufferData(_handle, out dataHandle);
540             MultimediaDebug.AssertNoError(ret);
541
542             Debug.Assert(dataHandle != IntPtr.Zero, "Data handle is invalid!");
543
544             int size = 0;
545             ret = Interop.MediaPacket.GetAllocatedBufferSize(_handle, out size);
546             MultimediaDebug.AssertNoError(ret);
547
548             return new MediaPacketBuffer(this, dataHandle, size);
549         }
550
551         /// <summary>
552         /// Creates an object of the MediaPacket with the specified <see cref="MediaFormat"/>.
553         /// </summary>
554         /// <param name="format">The media format for the new packet.</param>
555         /// <returns>A new MediaPacket object.</returns>
556         public static MediaPacket Create(MediaFormat format)
557         {
558             return new SimpleMediaPacket(format);
559         }
560
561         internal static MediaPacket From(IntPtr handle)
562         {
563             return new SimpleMediaPacket(handle);
564         }
565     }
566
567     internal class SimpleMediaPacket : MediaPacket
568     {
569         internal SimpleMediaPacket(MediaFormat format) : base(format)
570         {
571         }
572
573         internal SimpleMediaPacket(IntPtr handle) : base(handle)
574         {
575         }
576     }
577 }