[MediaTool] Add new Create API to support increasing ref count (#4321)
[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 an original MediaPacket.
56         /// </summary>
57         /// <param name="mediaPacket">The original MediaPacket.</param>
58         internal MediaPacket(MediaPacket mediaPacket) : this(mediaPacket._handle)
59         {
60             int ret = Native.Ref(_handle);
61             MultimediaDebug.AssertNoError(ret);
62         }
63
64         /// <summary>
65         /// Initializes a new instance of the MediaPacket class from a native handle.
66         /// </summary>
67         /// <param name="handle">The native handle to be used.</param>
68         internal MediaPacket(IntPtr handle)
69         {
70             _handle = handle;
71
72             int ret = Native.GetFormat(handle, out IntPtr formatHandle);
73             MultimediaDebug.AssertNoError(ret);
74
75             try
76             {
77                 if (formatHandle != IntPtr.Zero)
78                 {
79                     _format = MediaFormat.FromHandle(formatHandle);
80                 }
81
82                 _buffer = new Lazy<IMediaBuffer>(GetBuffer);
83             }
84             finally
85             {
86                 NativeFormat.Unref(formatHandle);
87             }
88         }
89
90         /// <summary>
91         /// Finalizes an instance of the MediaPacket class.
92         /// </summary>
93         ~MediaPacket()
94         {
95             Dispose(false);
96         }
97
98         /// <summary>
99         /// Creates and initializes a native handle for the current object.
100         /// </summary>
101         /// <param name="format">The format to be set to the media format.</param>
102         /// <exception cref="InvalidOperationException">Operation failed.</exception>
103         private void Initialize(MediaFormat format)
104         {
105             if (format.Type == MediaFormatType.Container)
106             {
107                 throw new ArgumentException("Container format can't be used to create a new packet.",
108                     nameof(format));
109             }
110
111             IntPtr formatHandle = IntPtr.Zero;
112
113             try
114             {
115                 formatHandle = format.AsNativeHandle();
116
117                 int ret = Native.New(formatHandle, IntPtr.Zero, IntPtr.Zero, out _handle);
118                 MultimediaDebug.AssertNoError(ret);
119
120                 Debug.Assert(_handle != IntPtr.Zero, "Created handle must not be null");
121
122                 Alloc();
123             }
124             catch (Exception)
125             {
126                 if (_handle != IntPtr.Zero)
127                 {
128                     Native.Unref(_handle);
129                     _handle = IntPtr.Zero;
130                 }
131
132                 throw;
133             }
134             finally
135             {
136                 if (formatHandle != IntPtr.Zero)
137                 {
138                     NativeFormat.Unref(formatHandle);
139                 }
140             }
141         }
142
143         /// <summary>
144         /// Allocates internal buffer.
145         /// </summary>
146         /// <exception cref="InvalidOperationException">Operation failed.</exception>
147         private void Alloc()
148         {
149             ErrorCode ret = (ErrorCode)Native.Alloc(_handle);
150             if (ret == ErrorCode.None)
151             {
152                 return;
153             }
154
155             _handle = IntPtr.Zero;
156
157             switch (ret)
158             {
159                 case ErrorCode.OutOfMemory:
160                     throw new OutOfMemoryException("Failed to allocate buffer for the packet.");
161
162                 default:
163                     throw new InvalidOperationException("Failed to create a packet.");
164             }
165         }
166
167         private readonly MediaFormat _format;
168
169         /// <summary>
170         /// Gets the media format of the current packet.
171         /// </summary>
172         /// <since_tizen> 3 </since_tizen>
173         public MediaFormat Format
174         {
175             get
176             {
177                 ValidateNotDisposed();
178                 return _format;
179             }
180         }
181
182         /// <summary>
183         /// Gets or sets the PTS(Presentation Time Stamp) value of the current packet in nanoseconds.
184         /// </summary>
185         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed.</exception>
186         /// <exception cref="InvalidOperationException">
187         ///     The MediaPacket is not in the writable state, which means it is being used by another module.
188         /// </exception>
189         /// <since_tizen> 3 </since_tizen>
190         public ulong Pts
191         {
192             get
193             {
194                 ValidateNotDisposed();
195
196                 int ret = Native.GetPts(_handle, out var value);
197                 MultimediaDebug.AssertNoError(ret);
198
199                 return value;
200             }
201             set
202             {
203                 ValidateNotDisposed();
204                 ValidateNotLocked();
205
206                 int ret = Native.SetPts(_handle, value);
207                 MultimediaDebug.AssertNoError(ret);
208             }
209         }
210
211         /// <summary>
212         /// Gets or sets the DTS(Decoding Time Stamp) value of the current packet in nanoseconds.
213         /// </summary>
214         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed.</exception>
215         /// <exception cref="InvalidOperationException">
216         ///     The MediaPacket is not in the writable state, which means it is being used by another module.
217         /// </exception>
218         /// <since_tizen> 3 </since_tizen>
219         public ulong Dts
220         {
221             get
222             {
223                 ValidateNotDisposed();
224
225                 int ret = Native.GetDts(_handle, out var value);
226                 MultimediaDebug.AssertNoError(ret);
227
228                 return value;
229             }
230             set
231             {
232                 ValidateNotDisposed();
233                 ValidateNotLocked();
234
235                 int ret = Native.SetDts(_handle, value);
236                 MultimediaDebug.AssertNoError(ret);
237             }
238         }
239
240         /// <summary>
241         /// Gets or sets the duration value of the current packet in nanoseconds.
242         /// </summary>
243         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed.</exception>
244         /// <exception cref="InvalidOperationException">
245         ///     The MediaPacket is not in the writable state, which means it is being used by another module.
246         /// </exception>
247         /// <since_tizen> 6 </since_tizen>
248         public ulong Duration
249         {
250             get
251             {
252                 ValidateNotDisposed();
253
254                 int ret = Native.GetDuration(_handle, out var value);
255                 MultimediaDebug.AssertNoError(ret);
256
257                 return value;
258             }
259             set
260             {
261                 ValidateNotDisposed();
262                 ValidateNotLocked();
263
264                 int ret = Native.SetDuration(_handle, value);
265                 MultimediaDebug.AssertNoError(ret);
266             }
267         }
268
269         /// <summary>
270         /// Gets a value indicating whether the packet is the encoded type.
271         /// </summary>
272         /// <value>true if the packet is the encoded type; otherwise, false.</value>
273         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed.</exception>
274         /// <since_tizen> 3 </since_tizen>
275         public bool IsEncoded
276         {
277             get
278             {
279                 ValidateNotDisposed();
280
281                 int ret = Native.IsEncoded(_handle, out var value);
282                 MultimediaDebug.AssertNoError(ret);
283
284                 return value;
285             }
286         }
287
288         /// <summary>
289         /// Gets or sets the rotation value of the current packet.
290         /// </summary>
291         /// <exception cref="ArgumentException">The specified value to set is invalid.</exception>
292         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed.</exception>
293         /// <exception cref="InvalidOperationException">
294         ///     The MediaPacket is not in the writable state, which means it is being used by another module.
295         /// </exception>
296         /// <since_tizen> 5 </since_tizen>
297         public Rotation Rotation
298         {
299             get
300             {
301                 ValidateNotDisposed();
302
303                 int ret = Native.GetRotation(_handle, out var value);
304                 MultimediaDebug.AssertNoError(ret);
305
306                 var rotation = value < RotationFlip.HorizontalFlip ? (Rotation)value : Rotation.Rotate0;
307
308                 return rotation;
309             }
310             set
311             {
312                 ValidateNotDisposed();
313                 ValidateNotLocked();
314                 ValidationUtil.ValidateEnum(typeof(Rotation), value, nameof(value));
315
316                 int ret = Native.SetRotation(_handle, (RotationFlip)value);
317                 MultimediaDebug.AssertNoError(ret);
318             }
319         }
320
321         /// <summary>
322         /// Gets or sets the flip value of the current packet.
323         /// </summary>
324         /// <remarks>
325         /// <see cref="Flips.None"/> will be ignored in set case. It's not supported in Native FW.
326         /// </remarks>
327         /// <exception cref="ArgumentException">The specified value to set is invalid.</exception>
328         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed.</exception>
329         /// <exception cref="InvalidOperationException">
330         ///     The MediaPacket is not in the writable state, which means it is being used by another module.
331         /// </exception>
332         /// <since_tizen> 5 </since_tizen>
333         public Flips Flip
334         {
335             get
336             {
337                 ValidateNotDisposed();
338
339                 int ret = Native.GetRotation(_handle, out var value);
340                 MultimediaDebug.AssertNoError(ret);
341
342                 var flip = (value < RotationFlip.HorizontalFlip) ? Flips.None :
343                     (value == RotationFlip.HorizontalFlip ? Flips.Horizontal : Flips.Vertical);
344
345                 return flip;
346             }
347             set
348             {
349                 ValidateNotDisposed();
350                 ValidateNotLocked();
351                 ValidationUtil.ValidateEnum(typeof(Flips), value, nameof(value));
352
353                 if (value == Flips.None)
354                 {
355                     return;
356                 }
357
358                 var flip = value == Flips.Horizontal ? RotationFlip.HorizontalFlip : RotationFlip.VerticalFlip;
359
360                 int ret = Native.SetRotation(_handle, flip);
361                 MultimediaDebug.AssertNoError(ret);
362             }
363         }
364
365         private Lazy<IMediaBuffer> _buffer;
366
367         /// <summary>
368         /// Gets the buffer of the packet.
369         /// </summary>
370         /// <value>
371         /// The <see cref="IMediaBuffer"/> allocated to the packet.
372         /// This property will return null if the packet is in the raw video format.
373         /// </value>
374         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed.</exception>
375         /// <seealso cref="IsEncoded"/>
376         /// <seealso cref="VideoPlanes"/>
377         /// <since_tizen> 3 </since_tizen>
378         public IMediaBuffer Buffer
379         {
380             get
381             {
382                 ValidateNotDisposed();
383
384                 if (IsVideoPlaneSupported)
385                 {
386                     return null;
387                 }
388
389                 return _buffer.Value;
390             }
391         }
392
393         /// <summary>
394         /// Gets or sets a length of data written in the <see cref="Buffer"/>.
395         /// </summary>
396         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed.</exception>
397         /// <exception cref="ArgumentOutOfRangeException">
398         ///     The value specified for this property is less than zero or greater than the length of the <see cref="Buffer"/>.</exception>
399         /// <exception cref="InvalidOperationException">
400         ///     The MediaPacket has <see cref="VideoPlanes"/> instead of <see cref="Buffer"/>.<br/>
401         ///     -or-<br/>
402         ///     The MediaPacket is not in the writable state, which means it is being used by another module.
403         ///     </exception>
404         /// <since_tizen> 3 </since_tizen>
405         public int BufferWrittenLength
406         {
407             get
408             {
409                 ValidateNotDisposed();
410
411                 int ret = Native.GetBufferSize(_handle, out var value);
412                 MultimediaDebug.AssertNoError(ret);
413
414                 Debug.Assert(value < int.MaxValue);
415
416                 return (int)value;
417             }
418             set
419             {
420                 ValidateNotDisposed();
421                 ValidateNotLocked();
422
423                 if (IsVideoPlaneSupported)
424                 {
425                     throw new InvalidOperationException(
426                         "This packet uses VideoPlanes instead of Buffer.");
427                 }
428
429                 Debug.Assert(Buffer != null);
430
431                 if (value < 0 || value >= Buffer.Length)
432                 {
433                     throw new ArgumentOutOfRangeException(nameof(value), value,
434                         "value must be less than Buffer.Size.");
435                 }
436
437                 int ret = Native.SetBufferSize(_handle, (ulong)value);
438                 MultimediaDebug.AssertNoError(ret);
439             }
440         }
441
442         private MediaPacketVideoPlane[] _videoPlanes;
443
444         /// <summary>
445         /// Gets the video planes of the packet.
446         /// </summary>
447         /// <value>The <see cref="MediaPacketVideoPlane"/>s allocated to the packet.
448         ///     This property will return null if the packet is not in the raw video format.</value>
449         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed.</exception>
450         /// <seealso cref="IsEncoded"/>
451         /// <seealso cref="Buffer"/>
452         /// <since_tizen> 3 </since_tizen>
453         public MediaPacketVideoPlane[] VideoPlanes
454         {
455             get
456             {
457                 ValidateNotDisposed();
458
459                 if (!IsVideoPlaneSupported)
460                 {
461                     return null;
462                 }
463
464                 if (_videoPlanes == null)
465                 {
466                     _videoPlanes = GetVideoPlanes();
467                 }
468
469                 return _videoPlanes;
470             }
471         }
472
473         /// <summary>
474         /// Gets or sets the buffer flags of the packet.
475         /// </summary>
476         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed.</exception>
477         /// <exception cref="InvalidOperationException">
478         ///     The MediaPacket is not in the writable state, which means it is being used by another module.
479         /// </exception>
480         /// <since_tizen> 3 </since_tizen>
481         public MediaPacketBufferFlags BufferFlags
482         {
483             get
484             {
485                 ValidateNotDisposed();
486
487                 int ret = Native.GetBufferFlags(_handle, out var value);
488                 MultimediaDebug.AssertNoError(ret);
489
490                 return value;
491             }
492             set
493             {
494                 ValidateNotDisposed();
495                 ValidateNotLocked();
496
497                 int ret = Native.ResetBufferFlags(_handle);
498                 MultimediaDebug.AssertNoError(ret);
499
500                 ret = Native.SetBufferFlags(_handle, (int)value);
501                 MultimediaDebug.AssertNoError(ret);
502             }
503         }
504
505         #region Dispose support
506         /// <summary>
507         /// Gets a value indicating whether the packet has been disposed.
508         /// </summary>
509         /// <value>true if the packet has been disposed of; otherwise, false.</value>
510         /// <since_tizen> 3 </since_tizen>
511         public bool IsDisposed => _isDisposed;
512
513         private bool _isDisposed = false;
514
515         /// <summary>
516         /// Releases all resources used by the <see cref="MediaPacket"/> object.
517         /// </summary>
518         /// <exception cref="InvalidOperationException">
519         ///     The MediaPacket can not be disposed, which means it is being used by another module.
520         /// </exception>
521         /// <since_tizen> 3 </since_tizen>
522         public void Dispose()
523         {
524             if (_isDisposed)
525             {
526                 return;
527             }
528             ValidateNotLocked();
529
530             Dispose(true);
531             GC.SuppressFinalize(this);
532         }
533
534         /// <summary>
535         /// Releases the resources used by the <see cref="MediaPacket"/> object.
536         /// </summary>
537         /// <param name="disposing">
538         /// true to release both managed and unmanaged resources; false to release only unmanaged resources.
539         /// </param>
540         /// <since_tizen> 3 </since_tizen>
541         protected virtual void Dispose(bool disposing)
542         {
543             if (_isDisposed)
544             {
545                 return;
546             }
547
548             if (_handle != IntPtr.Zero)
549             {
550                 Native.Unref(_handle);
551                 _handle = IntPtr.Zero;
552             }
553
554             _isDisposed = true;
555         }
556
557         /// <summary>
558         /// Validates the current object has not been disposed.
559         /// </summary>
560         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed.</exception>
561         private void ValidateNotDisposed()
562         {
563             if (_isDisposed)
564             {
565                 throw new ObjectDisposedException("This packet has already been disposed.");
566             }
567         }
568         #endregion
569
570         internal IntPtr GetHandle()
571         {
572             ValidateNotDisposed();
573
574             Debug.Assert(_handle != IntPtr.Zero, "The handle is invalid!");
575
576             return _handle;
577         }
578
579         /// <summary>
580         /// Ensures whether the packet is writable.
581         /// </summary>
582         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed.</exception>
583         /// <exception cref="InvalidOperationException">The MediaPacket is being used by another module.</exception>
584         internal void EnsureWritableState()
585         {
586             ValidateNotDisposed();
587             ValidateNotLocked();
588         }
589
590         /// <summary>
591         /// Ensures whether the packet is readable.
592         /// </summary>
593         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed.</exception>
594         internal void EnsureReadableState()
595         {
596             ValidateNotDisposed();
597         }
598
599         /// <summary>
600         /// Gets a value indicating whether the packet is in the raw video format.
601         /// </summary>
602         /// <value>true if the packet is in the raw video format; otherwise, false.</value>
603         private bool IsVideoPlaneSupported => !IsEncoded && Format.Type == MediaFormatType.Video;
604
605         /// <summary>
606         /// Retrieves video planes of the current packet.
607         /// </summary>
608         /// <returns>The <see cref="MediaPacketVideoPlane"/>s allocated to the current MediaPacket.</returns>
609         private MediaPacketVideoPlane[] GetVideoPlanes()
610         {
611             Debug.Assert(_handle != IntPtr.Zero, "The handle is invalid!");
612
613             int ret = Native.GetNumberOfVideoPlanes(_handle, out var numberOfPlanes);
614             MultimediaDebug.AssertNoError(ret);
615
616             MediaPacketVideoPlane[] planes = new MediaPacketVideoPlane[numberOfPlanes];
617
618             for (int i = 0; i < numberOfPlanes; ++i)
619             {
620                 planes[i] = new MediaPacketVideoPlane(this, i);
621             }
622
623             return planes;
624         }
625
626         /// <summary>
627         /// Retrieves the buffer of the current packet.
628         /// </summary>
629         /// <returns>The <see cref="IMediaBuffer"/> allocated to the current MediaPacket.</returns>
630         private IMediaBuffer GetBuffer()
631         {
632             Debug.Assert(!IsDisposed, "Packet is already disposed!");
633
634             Debug.Assert(_handle != IntPtr.Zero, "The handle is invalid!");
635
636             int ret = Native.GetBufferData(_handle, out var dataHandle);
637             MultimediaDebug.AssertNoError(ret);
638
639             Debug.Assert(dataHandle != IntPtr.Zero, "Data handle is invalid!");
640
641             ret = Native.GetAllocatedBufferSize(_handle, out var size);
642             MultimediaDebug.AssertNoError(ret);
643
644             Debug.Assert(size >= 0, "size must not be negative!");
645
646             return new DependentMediaBuffer(this, dataHandle, size);
647         }
648
649         /// <summary>
650         /// Creates an object of the MediaPacket with the specified <see cref="MediaFormat"/>.
651         /// </summary>
652         /// <param name="format">The media format for the new packet.</param>
653         /// <returns>A new MediaPacket object.</returns>
654         /// <since_tizen> 3 </since_tizen>
655         public static MediaPacket Create(MediaFormat format)
656         {
657             return new SimpleMediaPacket(format);
658         }
659
660         /// <summary>
661         /// Creates an object of the MediaPacket based on the original MediaPacket and increases the internal reference(hereafter ref) count by 1.
662         /// </summary>
663         /// <remarks>
664         /// This method can be useful when user share MediaPacket instance to other module and don't know the exact time to dispose it.\n
665         /// In this case, user creates a new instance with <see cref="Create(MediaPacket)"/> and shares it to other module.\n
666         /// And then, each MediaPacket instances can be disposed independently without concerning other instance's life cycle.
667         /// </remarks>
668         /// <param name="mediaPacket">The media packet to increase ref count.</param>
669         /// <returns>A MediaPacket object which is based on original MediaPacket instance.</returns>
670         /// <since_tizen> 10 </since_tizen>
671         public static MediaPacket Create(MediaPacket mediaPacket)
672         {
673             return new SimpleMediaPacket(mediaPacket);
674         }
675
676         internal static MediaPacket From(IntPtr handle)
677         {
678             return new SimpleMediaPacket(handle);
679         }
680
681         bool IBufferOwner.IsDisposed => IsDisposed;
682
683         bool IBufferOwner.IsBufferAccessible(object buffer, MediaBufferAccessMode accessMode) => true;
684     }
685
686     internal class SimpleMediaPacket : MediaPacket
687     {
688         internal SimpleMediaPacket(MediaFormat format) : base(format)
689         {
690         }
691
692         internal SimpleMediaPacket(IntPtr handle) : base(handle)
693         {
694         }
695
696         internal SimpleMediaPacket(MediaPacket mediaPacket) : base(mediaPacket)
697         {
698         }
699     }
700 }