43ba0c4b49ff1099b9149c55fe029c33f2c2fc58
[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 Tizen.Internals.Errors;
20 using Native = Tizen.Multimedia.Interop.MediaPacket;
21 using NativeFormat = Tizen.Multimedia.Interop.MediaFormat;
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             Initialize(format);
49
50             _format = format;
51             _buffer = new Lazy<IMediaBuffer>(GetBuffer);
52         }
53
54         /// <summary>
55         /// Initializes a new instance of the MediaPacket class from a native handle.
56         /// </summary>
57         /// <param name="handle">The native handle to be used.</param>
58         internal MediaPacket(IntPtr handle)
59         {
60             _handle = handle;
61
62             int ret = Native.GetFormat(handle, out IntPtr formatHandle);
63
64             MultimediaDebug.AssertNoError(ret);
65
66             try
67             {
68                 if (formatHandle != IntPtr.Zero)
69                 {
70                     _format = MediaFormat.FromHandle(formatHandle);
71                 }
72             }
73             finally
74             {
75                 NativeFormat.Unref(formatHandle);
76             }
77         }
78
79         /// <summary>
80         /// Finalizes an instance of the MediaPacket class.
81         /// </summary>
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("Container format can't be used to create a new packet.",
97                     nameof(format));
98             }
99
100             IntPtr formatHandle = IntPtr.Zero;
101
102             try
103             {
104                 formatHandle = format.AsNativeHandle();
105
106                 int ret = Native.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                     Native.Destroy(_handle);
118                     _handle = IntPtr.Zero;
119                 }
120
121                 throw;
122             }
123             finally
124             {
125                 if (formatHandle != IntPtr.Zero)
126                 {
127                     NativeFormat.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)Native.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         /// <since_tizen> 3 </since_tizen>
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         /// <since_tizen> 3 </since_tizen>
180         public ulong Pts
181         {
182             get
183             {
184                 ValidateNotDisposed();
185
186                 int ret = Native.GetPts(_handle, out var value);
187
188                 MultimediaDebug.AssertNoError(ret);
189
190                 return value;
191             }
192             set
193             {
194                 ValidateNotDisposed();
195                 ValidateNotLocked();
196
197                 int ret = Native.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         /// <since_tizen> 3 </since_tizen>
211         public ulong Dts
212         {
213             get
214             {
215                 ValidateNotDisposed();
216
217                 int ret = Native.GetDts(_handle, out var value);
218                 MultimediaDebug.AssertNoError(ret);
219
220                 return value;
221             }
222             set
223             {
224                 ValidateNotDisposed();
225                 ValidateNotLocked();
226
227                 int ret = Native.SetDts(_handle, value);
228                 MultimediaDebug.AssertNoError(ret);
229             }
230         }
231
232         /// <summary>
233         /// Gets or sets the duration value of the current packet.
234         /// </summary>
235         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed of.</exception>
236         /// <exception cref="InvalidOperationException">
237         ///     The MediaPacket is not in the writable state, which means it is being used by another module.
238         /// </exception>
239         /// <since_tizen> 6 </since_tizen>
240         public ulong Duration
241         {
242             get
243             {
244                 ValidateNotDisposed();
245
246                 int ret = Native.GetDuration(_handle, out var value);
247                 MultimediaDebug.AssertNoError(ret);
248
249                 return value;
250             }
251             set
252             {
253                 ValidateNotDisposed();
254                 ValidateNotLocked();
255
256                 int ret = Native.SetDuration(_handle, value);
257                 MultimediaDebug.AssertNoError(ret);
258             }
259         }
260
261         /// <summary>
262         /// Gets a value indicating whether the packet is the encoded type.
263         /// </summary>
264         /// <value>true if the packet is the encoded type; otherwise, false.</value>
265         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed of.</exception>
266         /// <since_tizen> 3 </since_tizen>
267         public bool IsEncoded
268         {
269             get
270             {
271                 ValidateNotDisposed();
272
273                 int ret = Native.IsEncoded(_handle, out var value);
274                 MultimediaDebug.AssertNoError(ret);
275
276                 return value;
277             }
278         }
279
280         /// <summary>
281         /// Gets or sets the rotation value of the current packet.
282         /// </summary>
283         /// <exception cref="ArgumentException">The specified value to set is invalid.</exception>
284         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed of.</exception>
285         /// <exception cref="InvalidOperationException">
286         ///     The MediaPacket is not in the writable state, which means it is being used by another module.
287         /// </exception>
288         /// <since_tizen> 5 </since_tizen>
289         public Rotation Rotation
290         {
291             get
292             {
293                 ValidateNotDisposed();
294
295                 int ret = Native.GetRotation(_handle, out var value);
296                 MultimediaDebug.AssertNoError(ret);
297
298                 var rotation = value < RotationFlip.HorizontalFlip ? (Rotation)value : Rotation.Rotate0;
299
300                 return rotation;
301             }
302             set
303             {
304                 ValidateNotDisposed();
305                 ValidateNotLocked();
306                 ValidationUtil.ValidateEnum(typeof(Rotation), value, nameof(value));
307
308                 int ret = Native.SetRotation(_handle, (RotationFlip)value);
309                 MultimediaDebug.AssertNoError(ret);
310             }
311         }
312
313         /// <summary>
314         /// Gets or sets the flip value of the current packet.
315         /// </summary>
316         /// <remarks>
317         /// <see cref="Flips.None"/> will be ignored in set case. It's not supported in Native FW.
318         /// </remarks>
319         /// <exception cref="ArgumentException">The specified value to set is invalid.</exception>
320         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed of.</exception>
321         /// <exception cref="InvalidOperationException">
322         ///     The MediaPacket is not in the writable state, which means it is being used by another module.
323         /// </exception>
324         /// <since_tizen> 5 </since_tizen>
325         public Flips Flip
326         {
327             get
328             {
329                 ValidateNotDisposed();
330
331                 int ret = Native.GetRotation(_handle, out var value);
332                 MultimediaDebug.AssertNoError(ret);
333
334                 var flip = (value < RotationFlip.HorizontalFlip) ? Flips.None :
335                     (value == RotationFlip.HorizontalFlip ? Flips.Horizontal : Flips.Vertical);
336
337                 return flip;
338             }
339             set
340             {
341                 ValidateNotDisposed();
342                 ValidateNotLocked();
343                 ValidationUtil.ValidateEnum(typeof(Flips), value, nameof(value));
344
345                 if (value == Flips.None)
346                 {
347                     return;
348                 }
349
350                 var flip = value == Flips.Horizontal ? RotationFlip.HorizontalFlip : RotationFlip.VerticalFlip;
351
352                 int ret = Native.SetRotation(_handle, flip);
353                 MultimediaDebug.AssertNoError(ret);
354             }
355         }
356
357         private Lazy<IMediaBuffer> _buffer;
358
359         /// <summary>
360         /// Gets the buffer of the packet.
361         /// </summary>
362         /// <value>
363         /// The <see cref="IMediaBuffer"/> allocated to the packet.
364         /// This property will return null if the packet is in the raw video format.
365         /// </value>
366         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed of.</exception>
367         /// <seealso cref="IsEncoded"/>
368         /// <seealso cref="VideoPlanes"/>
369         /// <since_tizen> 3 </since_tizen>
370         public IMediaBuffer Buffer
371         {
372             get
373             {
374                 ValidateNotDisposed();
375
376                 if (IsVideoPlaneSupported)
377                 {
378                     return null;
379                 }
380
381                 return _buffer.Value;
382             }
383         }
384
385         /// <summary>
386         /// Gets or sets a length of data written in the <see cref="Buffer"/>.
387         /// </summary>
388         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed of.</exception>
389         /// <exception cref="ArgumentOutOfRangeException">
390         ///     The value specified for this property is less than zero or greater than the length of the <see cref="Buffer"/>.</exception>
391         /// <exception cref="InvalidOperationException">
392         ///     The MediaPacket has <see cref="VideoPlanes"/> instead of <see cref="Buffer"/>.<br/>
393         ///     -or-<br/>
394         ///     The MediaPacket is not in the writable state, which means it is being used by another module.
395         ///     </exception>
396         /// <since_tizen> 3 </since_tizen>
397         public int BufferWrittenLength
398         {
399             get
400             {
401                 ValidateNotDisposed();
402
403                 int ret = Native.GetBufferSize(_handle, out var value);
404                 MultimediaDebug.AssertNoError(ret);
405
406                 Debug.Assert(value < int.MaxValue);
407
408                 return (int)value;
409             }
410             set
411             {
412                 ValidateNotDisposed();
413                 ValidateNotLocked();
414
415                 if (IsVideoPlaneSupported)
416                 {
417                     throw new InvalidOperationException(
418                         "This packet uses VideoPlanes instead of Buffer.");
419                 }
420
421                 Debug.Assert(Buffer != null);
422
423                 if (value < 0 || value >= Buffer.Length)
424                 {
425                     throw new ArgumentOutOfRangeException(nameof(value), value,
426                         "value must be less than Buffer.Size.");
427                 }
428
429                 int ret = Native.SetBufferSize(_handle, (ulong)value);
430                 MultimediaDebug.AssertNoError(ret);
431             }
432         }
433
434         private MediaPacketVideoPlane[] _videoPlanes;
435
436         /// <summary>
437         /// Gets the video planes of the packet.
438         /// </summary>
439         /// <value>The <see cref="MediaPacketVideoPlane"/>s allocated to the packet.
440         ///     This property will return null if the packet is not in the raw video format.</value>
441         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed of.</exception>
442         /// <seealso cref="IsEncoded"/>
443         /// <seealso cref="Buffer"/>
444         /// <since_tizen> 3 </since_tizen>
445         public MediaPacketVideoPlane[] VideoPlanes
446         {
447             get
448             {
449                 ValidateNotDisposed();
450
451                 if (!IsVideoPlaneSupported)
452                 {
453                     return null;
454                 }
455
456                 if (_videoPlanes == null)
457                 {
458                     _videoPlanes = GetVideoPlanes();
459                 }
460
461                 return _videoPlanes;
462             }
463         }
464
465         /// <summary>
466         /// Gets or sets the buffer flags of the packet.
467         /// </summary>
468         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed of.</exception>
469         /// <exception cref="InvalidOperationException">
470         ///     The MediaPacket is not in the writable state, which means it is being used by another module.
471         /// </exception>
472         /// <since_tizen> 3 </since_tizen>
473         public MediaPacketBufferFlags BufferFlags
474         {
475             get
476             {
477                 ValidateNotDisposed();
478
479                 int ret = Native.GetBufferFlags(_handle, out var value);
480
481                 MultimediaDebug.AssertNoError(ret);
482
483                 return value;
484             }
485
486             set
487             {
488                 ValidateNotDisposed();
489                 ValidateNotLocked();
490
491                 int ret = Native.ResetBufferFlags(_handle);
492
493                 MultimediaDebug.AssertNoError(ret);
494
495                 ret = Native.SetBufferFlags(_handle, (int)value);
496
497                 MultimediaDebug.AssertNoError(ret);
498             }
499         }
500
501         #region Dispose support
502         /// <summary>
503         /// Gets a value indicating whether the packet has been disposed of.
504         /// </summary>
505         /// <value>true if the packet has been disposed of; otherwise, false.</value>
506         /// <since_tizen> 3 </since_tizen>
507         public bool IsDisposed => _isDisposed;
508
509         private bool _isDisposed = false;
510
511         /// <summary>
512         /// Releases all resources used by the <see cref="MediaPacket"/> object.
513         /// </summary>
514         /// <exception cref="InvalidOperationException">
515         ///     The MediaPacket can not be disposed, which means it is being used by another module.
516         /// </exception>
517         /// <since_tizen> 3 </since_tizen>
518         public void Dispose()
519         {
520             if (_isDisposed)
521             {
522                 return;
523             }
524             ValidateNotLocked();
525
526             Dispose(true);
527             GC.SuppressFinalize(this);
528         }
529
530         /// <summary>
531         /// Releases the resources used by the <see cref="MediaPacket"/> object.
532         /// </summary>
533         /// <param name="disposing">
534         /// true to release both managed and unmanaged resources; false to release only unmanaged resources.
535         /// </param>
536         /// <since_tizen> 3 </since_tizen>
537         protected virtual void Dispose(bool disposing)
538         {
539             if (_isDisposed)
540             {
541                 return;
542             }
543
544             if (_handle != IntPtr.Zero)
545             {
546                 Native.Destroy(_handle);
547                 _handle = IntPtr.Zero;
548             }
549
550             _isDisposed = true;
551         }
552
553         /// <summary>
554         /// Validates the current object has not been disposed of.
555         /// </summary>
556         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed of.</exception>
557         private void ValidateNotDisposed()
558         {
559             if (_isDisposed)
560             {
561                 throw new ObjectDisposedException("This packet has already been disposed of.");
562             }
563         }
564         #endregion
565
566         internal IntPtr GetHandle()
567         {
568             ValidateNotDisposed();
569
570             Debug.Assert(_handle != IntPtr.Zero, "The handle is invalid!");
571
572             return _handle;
573         }
574
575         /// <summary>
576         /// Ensures whether the packet is writable.
577         /// </summary>
578         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed of.</exception>
579         /// <exception cref="InvalidOperationException">The MediaPacket is being used by another module.</exception>
580         internal void EnsureWritableState()
581         {
582             ValidateNotDisposed();
583             ValidateNotLocked();
584         }
585
586         /// <summary>
587         /// Ensures whether the packet is readable.
588         /// </summary>
589         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed of.</exception>
590         internal void EnsureReadableState()
591         {
592             ValidateNotDisposed();
593         }
594
595         /// <summary>
596         /// Gets a value indicating whether the packet is in the raw video format.
597         /// </summary>
598         /// <value>true if the packet is in the raw video format; otherwise, false.</value>
599         private bool IsVideoPlaneSupported => !IsEncoded && Format.Type == MediaFormatType.Video;
600
601         /// <summary>
602         /// Retrieves video planes of the current packet.
603         /// </summary>
604         /// <returns>The <see cref="MediaPacketVideoPlane"/>s allocated to the current MediaPacket.</returns>
605         private MediaPacketVideoPlane[] GetVideoPlanes()
606         {
607             Debug.Assert(_handle != IntPtr.Zero, "The handle is invalid!");
608
609             int ret = Native.GetNumberOfVideoPlanes(_handle, out var numberOfPlanes);
610
611             MultimediaDebug.AssertNoError(ret);
612
613             MediaPacketVideoPlane[] planes = new MediaPacketVideoPlane[numberOfPlanes];
614
615             for (int i = 0; i < numberOfPlanes; ++i)
616             {
617                 planes[i] = new MediaPacketVideoPlane(this, i);
618             }
619
620             return planes;
621         }
622
623         /// <summary>
624         /// Retrieves the buffer of the current packet.
625         /// </summary>
626         /// <returns>The <see cref="IMediaBuffer"/> allocated to the current MediaPacket.</returns>
627         private IMediaBuffer GetBuffer()
628         {
629             Debug.Assert(!IsDisposed, "Packet is already disposed!");
630
631             Debug.Assert(_handle != IntPtr.Zero, "The handle is invalid!");
632
633             int ret = Native.GetBufferData(_handle, out var dataHandle);
634             MultimediaDebug.AssertNoError(ret);
635
636             Debug.Assert(dataHandle != IntPtr.Zero, "Data handle is invalid!");
637
638             ret = Native.GetAllocatedBufferSize(_handle, out var size);
639             MultimediaDebug.AssertNoError(ret);
640
641             Debug.Assert(size >= 0, "size must not be negative!");
642
643             return new DependentMediaBuffer(this, dataHandle, size);
644         }
645
646         /// <summary>
647         /// Creates an object of the MediaPacket with the specified <see cref="MediaFormat"/>.
648         /// </summary>
649         /// <param name="format">The media format for the new packet.</param>
650         /// <returns>A new MediaPacket object.</returns>
651         /// <since_tizen> 3 </since_tizen>
652         public static MediaPacket Create(MediaFormat format)
653         {
654             return new SimpleMediaPacket(format);
655         }
656
657         internal static MediaPacket From(IntPtr handle)
658         {
659             return new SimpleMediaPacket(handle);
660         }
661
662         bool IBufferOwner.IsDisposed => IsDisposed;
663
664         bool IBufferOwner.IsBufferAccessible(object buffer, MediaBufferAccessMode accessMode) => true;
665     }
666
667     internal class SimpleMediaPacket : MediaPacket
668     {
669         internal SimpleMediaPacket(MediaFormat format) : base(format)
670         {
671         }
672
673         internal SimpleMediaPacket(IntPtr handle) : base(handle)
674         {
675         }
676     }
677 }