Release 4.0.0-preview1-00235
[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 : IBufferOwner, 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             _buffer = new Lazy<IMediaBuffer>(GetBuffer);
56         }
57
58         /// <summary>
59         /// Initializes a new instance of the MediaPacket class from a native handle.
60         /// </summary>
61         /// <param name="handle">The native handle to be used.</param>
62         internal MediaPacket(IntPtr handle)
63         {
64             _handle = handle;
65
66             IntPtr formatHandle;
67             int ret = Interop.MediaPacket.GetFormat(handle, out formatHandle);
68
69             MultimediaDebug.AssertNoError(ret);
70
71             try
72             {
73                 if (formatHandle != IntPtr.Zero)
74                 {
75                     _format = MediaFormat.FromHandle(formatHandle);
76                 }
77             }
78             finally
79             {
80                 Interop.MediaFormat.Unref(formatHandle);
81             }
82         }
83
84         ~MediaPacket()
85         {
86             Dispose(false);
87         }
88
89         /// <summary>
90         /// Creates and initializes a native handle for the current object.
91         /// </summary>
92         /// <param name="format">The format to be set to the media format.</param>
93         /// <exception cref="InvalidOperationException">Operation failed.</exception>
94         private void Initialize(MediaFormat format)
95         {
96             if (format.Type == MediaFormatType.Container)
97             {
98                 throw new ArgumentException("Creating a packet for container is not supported.");
99             }
100
101             IntPtr formatHandle = IntPtr.Zero;
102
103             try
104             {
105                 formatHandle = format.AsNativeHandle();
106
107                 int ret = Interop.MediaPacket.Create(formatHandle, IntPtr.Zero, IntPtr.Zero, out _handle);
108                 MultimediaDebug.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="InvalidOperationException">Operation failed.</exception>
137         private void Alloc()
138         {
139             ErrorCode ret = (ErrorCode)Interop.MediaPacket.Alloc(_handle);
140             if (ret == ErrorCode.None)
141             {
142                 return;
143             }
144
145             _handle = IntPtr.Zero;
146
147             switch (ret)
148             {
149                 case ErrorCode.OutOfMemory:
150                     throw new OutOfMemoryException("Failed to allocate buffer for the packet.");
151
152                 default:
153                     throw new InvalidOperationException("Failed to create a packet.");
154             }
155
156         }
157
158         private readonly MediaFormat _format;
159
160         /// <summary>
161         /// Gets the media format of the current packet.
162         /// </summary>
163         public MediaFormat Format
164         {
165             get
166             {
167                 ValidateNotDisposed();
168                 return _format;
169             }
170         }
171
172         /// <summary>
173         /// Gets or sets the PTS(Presentation Time Stamp) value of the current packet.
174         /// </summary>
175         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed of.</exception>
176         /// <exception cref="InvalidOperationException">
177         ///     The MediaPacket is not in the writable state, which means it is being used by another module.
178         /// </exception>
179         public ulong Pts
180         {
181             get
182             {
183                 ValidateNotDisposed();
184
185                 ulong value = 0;
186                 int ret = Interop.MediaPacket.GetPts(_handle, out value);
187
188                 MultimediaDebug.AssertNoError(ret);
189
190                 return value;
191             }
192             set
193             {
194                 ValidateNotDisposed();
195                 ValidateNotLocked();
196
197                 int ret = Interop.MediaPacket.SetPts(_handle, value);
198
199                 MultimediaDebug.AssertNoError(ret);
200             }
201         }
202
203         /// <summary>
204         /// Gets or sets the DTS(Decoding Time Stamp) value of the current packet.
205         /// </summary>
206         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed of.</exception>
207         /// <exception cref="InvalidOperationException">
208         ///     The MediaPacket is not in the writable state, which means it is being used by another module.
209         /// </exception>
210         public ulong Dts
211         {
212             get
213             {
214                 ValidateNotDisposed();
215
216                 ulong value = 0;
217                 int ret = Interop.MediaPacket.GetDts(_handle, out value);
218
219                 MultimediaDebug.AssertNoError(ret);
220
221                 return value;
222             }
223             set
224             {
225                 ValidateNotDisposed();
226                 ValidateNotLocked();
227
228                 int ret = Interop.MediaPacket.SetDts(_handle, value);
229
230                 MultimediaDebug.AssertNoError(ret);
231             }
232         }
233
234         /// <summary>
235         /// Gets a value indicating whether the packet is the encoded type.
236         /// </summary>
237         /// <value>true if the packet is the encoded type; otherwise, false.</value>
238         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed of.</exception>
239         public bool IsEncoded
240         {
241             get
242             {
243                 ValidateNotDisposed();
244
245                 bool value = false;
246                 int ret = Interop.MediaPacket.IsEncoded(_handle, out value);
247
248                 MultimediaDebug.AssertNoError(ret);
249
250                 return value;
251             }
252         }
253
254         private Lazy<IMediaBuffer> _buffer;
255
256         /// <summary>
257         /// Gets the buffer of the packet.
258         /// </summary>
259         /// <value>
260         /// The <see cref="IMediaBuffer"/> allocated to the packet.
261         /// This property will return null if the packet is in the raw video format.
262         /// </value>
263         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed of.</exception>
264         /// <seealso cref="IsEncoded"/>
265         /// <seealso cref="VideoPlanes"/>
266         public IMediaBuffer Buffer
267         {
268             get
269             {
270                 ValidateNotDisposed();
271
272                 if (IsVideoPlaneSupported)
273                 {
274                     return null;
275                 }
276
277                 return _buffer.Value;
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 of.</exception>
285         /// <exception cref="ArgumentOutOfRangeException">
286         ///     The value specified for this property is less than zero or greater than the length of the <see cref="Buffer"/>.</exception>
287         /// <exception cref="InvalidOperationException">
288         ///     The MediaPacket has <see cref="VideoPlanes"/> instead of <see cref="Buffer"/>.\n
289         ///     -or-\n
290         ///     The MediaPacket is not in the writable state, which means it is 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                 MultimediaDebug.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                 MultimediaDebug.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 in the raw video format.</value>
336         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed of.</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 of.</exception>
363         /// <exception cref="InvalidOperationException">
364         ///     The MediaPacket is not in the writable state, which means it is being used by another module.
365         /// </exception>
366         public MediaPacketBufferFlags BufferFlags
367         {
368             get
369             {
370                 ValidateNotDisposed();
371
372                 int ret = Interop.MediaPacket.GetBufferFlags(_handle, out var value);
373
374                 MultimediaDebug.AssertNoError(ret);
375
376                 return value;
377             }
378
379             set
380             {
381                 ValidateNotDisposed();
382                 ValidateNotLocked();
383
384                 int ret = Interop.MediaPacket.ResetBufferFlags(_handle);
385
386                 MultimediaDebug.AssertNoError(ret);
387
388                 ret = Interop.MediaPacket.SetBufferFlags(_handle, (int)value);
389
390                 MultimediaDebug.AssertNoError(ret);
391             }
392         }
393
394         /// <summary>
395         /// Gets a value indicating whether the packet has been disposed of.
396         /// </summary>
397         /// <value>true if the packet has been disposed of; otherwise, false.</value>
398         public bool IsDisposed => _isDisposed;
399
400         private bool _isDisposed = false;
401
402
403         /// <summary>
404         /// Releases all resources used by the <see cref="MediaPacket"/> object.
405         /// </summary>
406         /// <exception cref="InvalidOperationException">
407         ///     The MediaPacket can not be disposed, which means it is being used by another module.
408         /// </exception>
409         public void Dispose()
410         {
411             if (_isDisposed)
412             {
413                 return;
414             }
415             ValidateNotLocked();
416
417             Dispose(true);
418             GC.SuppressFinalize(this);
419         }
420
421         /// <summary>
422         /// Releases the resources used by the <see cref="MediaPacket"/> object.
423         /// </summary>
424         /// <param name="disposing">
425         /// true to release both managed and unmanaged resources; false to release only unmanaged resources.
426         /// </param>
427         protected virtual void Dispose(bool disposing)
428         {
429             if (_isDisposed)
430             {
431                 return;
432             }
433
434             if (_handle != IntPtr.Zero)
435             {
436                 Interop.MediaPacket.Destroy(_handle);
437                 _handle = IntPtr.Zero;
438             }
439
440             _isDisposed = true;
441         }
442
443         internal IntPtr GetHandle()
444         {
445             ValidateNotDisposed();
446
447             Debug.Assert(_handle != IntPtr.Zero, "The handle is invalid!");
448
449             return _handle;
450         }
451
452         /// <summary>
453         /// Validates the current object has not been disposed of.
454         /// </summary>
455         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed of.</exception>
456         private void ValidateNotDisposed()
457         {
458             if (_isDisposed)
459             {
460                 throw new ObjectDisposedException("This packet has already been disposed of.");
461             }
462         }
463
464         /// <summary>
465         /// Ensures whether the packet is writable.
466         /// </summary>
467         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed of.</exception>
468         /// <exception cref="InvalidOperationException">The MediaPacket is being used by another module.</exception>
469         internal void EnsureWritableState()
470         {
471             ValidateNotDisposed();
472             ValidateNotLocked();
473         }
474
475         /// <summary>
476         /// Ensures whether the packet is readable.
477         /// </summary>
478         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed of.</exception>
479         internal void EnsureReadableState()
480         {
481             ValidateNotDisposed();
482         }
483
484         /// <summary>
485         /// Gets a value indicating whether the packet is in the raw video format.
486         /// </summary>
487         /// <value>true if the packet is in the raw video format; otherwise, false.</value>
488         private bool IsVideoPlaneSupported
489         {
490             get
491             {
492                 return !IsEncoded && Format.Type == MediaFormatType.Video;
493             }
494         }
495
496         /// <summary>
497         /// Retrieves video planes of the current packet.
498         /// </summary>
499         /// <returns>The <see cref="MediaPacketVideoPlane"/>s allocated to the current MediaPacket.</returns>
500         private MediaPacketVideoPlane[] GetVideoPlanes()
501         {
502             Debug.Assert(_handle != IntPtr.Zero, "The handle is invalid!");
503
504             uint numberOfPlanes = 0;
505             int ret = Interop.MediaPacket.GetNumberOfVideoPlanes(_handle, out numberOfPlanes);
506
507             MultimediaDebug.AssertNoError(ret);
508
509             MediaPacketVideoPlane[] planes = new MediaPacketVideoPlane[numberOfPlanes];
510
511             for (int i = 0; i < numberOfPlanes; ++i)
512             {
513                 planes[i] = new MediaPacketVideoPlane(this, i);
514             }
515
516             return planes;
517         }
518
519         /// <summary>
520         /// Retrieves the buffer of the current packet.
521         /// </summary>
522         /// <returns>The <see cref="IMediaBuffer"/> allocated to the current MediaPacket.</returns>
523         private IMediaBuffer GetBuffer()
524         {
525             Debug.Assert(!IsDisposed, "Packet is already disposed!");
526
527             Debug.Assert(_handle != IntPtr.Zero, "The handle is invalid!");
528
529             IntPtr dataHandle;
530
531             int ret = Interop.MediaPacket.GetBufferData(_handle, out dataHandle);
532             MultimediaDebug.AssertNoError(ret);
533
534             Debug.Assert(dataHandle != IntPtr.Zero, "Data handle is invalid!");
535
536             int size = 0;
537             ret = Interop.MediaPacket.GetAllocatedBufferSize(_handle, out size);
538             MultimediaDebug.AssertNoError(ret);
539
540             Debug.Assert(size >= 0, "size must not be negative!");
541
542             return new DependentMediaBuffer(this, dataHandle, size);
543         }
544
545         /// <summary>
546         /// Creates an object of the MediaPacket with the specified <see cref="MediaFormat"/>.
547         /// </summary>
548         /// <param name="format">The media format for the new packet.</param>
549         /// <returns>A new MediaPacket object.</returns>
550         public static MediaPacket Create(MediaFormat format)
551         {
552             return new SimpleMediaPacket(format);
553         }
554
555         internal static MediaPacket From(IntPtr handle)
556         {
557             return new SimpleMediaPacket(handle);
558         }
559
560         bool IBufferOwner.IsDisposed => IsDisposed;
561
562         bool IBufferOwner.IsBufferAccessible(object buffer, MediaBufferAccessMode accessMode) => true;
563     }
564
565     internal class SimpleMediaPacket : MediaPacket
566     {
567         internal SimpleMediaPacket(MediaFormat format) : base(format)
568         {
569         }
570
571         internal SimpleMediaPacket(IntPtr handle) : base(handle)
572         {
573         }
574     }
575 }