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