[MediaTool] Change pinvoke method (#2975)
[platform/core/csapi/tizenfx.git] / src / Tizen.Multimedia / MediaTool / AudioMediaFormat.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 using System;
17 using System.Collections.Generic;
18 using System.Collections.ObjectModel;
19 using System.Linq;
20 using System.Diagnostics;
21 using Tizen.Internals.Errors;
22 using Native = Tizen.Multimedia.Interop.MediaFormat;
23
24 namespace Tizen.Multimedia
25 {
26     /// <summary>
27     /// Represents an audio media format. This class cannot be inherited.
28     /// </summary>
29     /// <since_tizen> 3 </since_tizen>
30     public sealed class AudioMediaFormat : MediaFormat
31     {
32         /// <summary>
33         /// Initializes a new instance of the AudioMediaFormat class with the specified mime type,
34         /// channel, sample rate, bit, and bit rate.
35         /// </summary>
36         /// <param name="mimeType">The mime type of the format.</param>
37         /// <param name="channel">The channel value of the format.</param>
38         /// <param name="sampleRate">The sample rate value of the format.</param>
39         /// <param name="bit">The bit value of the format.</param>
40         /// <param name="bitRate">The bit rate value of the format.</param>
41         /// <exception cref="ArgumentException"><paramref name="mimeType"/> is invalid(i.e. undefined value).</exception>
42         /// <exception cref="ArgumentOutOfRangeException">
43         ///     <paramref name="channel"/>, <paramref name="sampleRate"/>, <paramref name="bit"/>, or <paramref name="bitRate"/> is less than zero.
44         /// </exception>
45         /// <since_tizen> 3 </since_tizen>
46         public AudioMediaFormat(MediaFormatAudioMimeType mimeType,
47             int channel, int sampleRate, int bit, int bitRate)
48             : this(mimeType, channel, sampleRate, bit, bitRate, MediaFormatAacType.None)
49         {
50         }
51
52         /// <summary>
53         /// Initializes a new instance of the AudioMediaFormat class with the specified mime type,
54         ///     channel, sample rate, bit, bit rate, and AAC type.
55         /// </summary>
56         /// <param name="mimeType">The mime type of the format.</param>
57         /// <param name="channel">The channel value of the format.</param>
58         /// <param name="sampleRate">The sample rate value of the format.</param>
59         /// <param name="bit">The bit value of the format.</param>
60         /// <param name="bitRate">The bit rate value of the format.</param>
61         /// <param name="aacType">The AAC bitstream format(ADIF or ADTS).</param>
62         /// <exception cref="ArgumentException">
63         ///     <paramref name="mimeType"/> or <paramref name="aacType"/> is invalid (i.e. undefined value).<br/>
64         ///     -or-<br/>
65         ///     <paramref name="aacType"/> is not <see cref="MediaFormatAacType.None"/>, but <paramref name="mimeType"/> is one of the AAC types.
66         ///     </exception>
67         /// <exception cref="ArgumentOutOfRangeException">
68         ///     <paramref name="channel"/>, <paramref name="sampleRate"/>, <paramref name="bit"/>, or <paramref name="bitRate"/> is less than zero.
69         /// </exception>
70         /// <since_tizen> 3 </since_tizen>
71         public AudioMediaFormat(MediaFormatAudioMimeType mimeType,
72             int channel, int sampleRate, int bit, int bitRate, MediaFormatAacType aacType)
73             : this(mimeType, channel, sampleRate, bit, bitRate, aacType, 0, null)
74         {
75         }
76
77         /// <summary>
78         /// Initializes a new instance of the AudioMediaFormat class with the specified mime type,
79         /// channel, sample rate, bit, bit rate, bit depth, and audio channel map.
80         /// </summary>
81         /// <remarks>
82         /// If <paramref name="audioChannelMap"/> contains <see cref="MediaFormatAudioChannelPosition.None"/>,
83         /// <paramref name="channel"/> should be set greater than 0.<br/>
84         /// If <paramref name="audioChannelMap"/> contains <see cref="MediaFormatAudioChannelPosition.Mono"/>,
85         /// <paramref name="channel"/> should be set 1.<br/>
86         /// User can not set <see cref="MediaFormatAudioChannelPosition.None"/> with another channel positions.<br/>
87         /// User can not set <see cref="MediaFormatAudioChannelPosition.Mono"/> with another channel positions.<br/>
88         /// If same channel position is added in <paramref name="audioChannelMap"/> more than once, the duplicaiton will be removed.
89         /// </remarks>
90         /// <param name="mimeType">The mime type of the format.</param>
91         /// <param name="channel">The channel value of the format.</param>
92         /// <param name="sampleRate">The sample rate value of the format.</param>
93         /// <param name="bit">The bit value of the format.</param>
94         /// <param name="bitRate">The bit rate value of the format.</param>
95         /// <param name="bitDepth">The bit depth value of the PCM audio format.</param>
96         /// <param name="audioChannelMap">The loudspeaker position in PCM audio format.</param>
97         /// <exception cref="ArgumentException">
98         ///     <paramref name="mimeType"/> is invalid (i.e. undefined value).<br/>
99         /// </exception>
100         /// <exception cref="ArgumentException">
101         /// <paramref name="audioChannelMap"/> is invalid or mismatched with <paramref name="channel"/> like the following:<br/>
102         ///     <paramref name="audioChannelMap"/> is not matched correctly with <paramref name="channel"/>.
103         ///     -or-<br/>
104         ///     <paramref name="audioChannelMap"/> is set to <see cref="MediaFormatAudioChannelPosition.Invaild"/>.
105         ///     -or-<br/>
106         ///     <see cref="MediaFormatAudioChannelPosition.Mono"/> or <see cref="MediaFormatAudioChannelPosition.None"/> is set with another channel position.
107         /// </exception>
108         /// <exception cref="ArgumentOutOfRangeException">
109         ///     <paramref name="channel"/>, <paramref name="sampleRate"/>, <paramref name="bit"/>, or <paramref name="bitRate"/>,
110         ///     <paramref name="bitDepth"/> is less than zero.
111         /// </exception>
112         /// <since_tizen> 6 </since_tizen>
113         public AudioMediaFormat(MediaFormatAudioMimeType mimeType,
114             int channel, int sampleRate, int bit, int bitRate, int bitDepth, IList<MediaFormatAudioChannelPosition> audioChannelMap)
115             : this(mimeType, channel, sampleRate, bit, bitRate, MediaFormatAacType.None, bitDepth, audioChannelMap)
116         {
117         }
118
119         /// <summary>
120         /// Initializes a new instance of the AudioMediaFormat class with the specified mime type,
121         /// channel, sample rate, bit, bit rate, bit depth, and audio channel map.
122         /// </summary>
123         /// <remarks>
124         /// If <paramref name="audioChannelMap"/> contains <see cref="MediaFormatAudioChannelPosition.None"/>,
125         /// <paramref name="channel"/> should be set greater than 0.<br/>
126         /// If <paramref name="audioChannelMap"/> contains <see cref="MediaFormatAudioChannelPosition.Mono"/>,
127         /// <paramref name="channel"/> should be set 1.<br/>
128         /// User can not set <see cref="MediaFormatAudioChannelPosition.None"/> with another channel positions.<br/>
129         /// User can not set <see cref="MediaFormatAudioChannelPosition.Mono"/> with another channel positions.<br/>
130         /// If same channel position is added in <paramref name="audioChannelMap"/> more than twice, its duplicaiton will be removed.
131         /// </remarks>
132         /// <param name="mimeType">The mime type of the format.</param>
133         /// <param name="channel">The channel value of the format.</param>
134         /// <param name="sampleRate">The sample rate value of the format.</param>
135         /// <param name="bit">The bit value of the format.</param>
136         /// <param name="bitRate">The bit rate value of the format.</param>
137         /// <param name="aacType">The AAC bitstream format(ADIF or ADTS).</param>
138         /// <param name="bitDepth">The bit depth value of the PCM audio format.</param>
139         /// <param name="audioChannelMap">The loudspeaker position in PCM audio format.</param>
140         /// <exception cref="ArgumentException">
141         ///     <paramref name="mimeType"/> or <paramref name="aacType"/> is invalid (i.e. undefined value).<br/>
142         ///     -or-<br/>
143         ///     <paramref name="aacType"/> is not <see cref="MediaFormatAacType.None"/>, but <paramref name="mimeType"/> is one of the AAC types.
144         /// </exception>
145         /// <exception cref="ArgumentException">
146         /// <paramref name="audioChannelMap"/> is invalid or mismatched with <paramref name="channel"/> like the following:<br/>
147         ///     <paramref name="audioChannelMap"/> is not matched correctly with <paramref name="channel"/>.
148         ///     -or-<br/>
149         ///     <paramref name="audioChannelMap"/> is set to <see cref="MediaFormatAudioChannelPosition.Invaild"/>.
150         ///     -or-<br/>
151         ///     <see cref="MediaFormatAudioChannelPosition.Mono"/> or <see cref="MediaFormatAudioChannelPosition.None"/> is set with another channel position.
152         /// </exception>
153         /// <exception cref="ArgumentOutOfRangeException">
154         ///     <paramref name="channel"/>, <paramref name="sampleRate"/>, <paramref name="bit"/>, or <paramref name="bitRate"/>,
155         ///     <paramref name="bitDepth"/> is less than zero.
156         /// </exception>
157         /// <since_tizen> 6 </since_tizen>
158         public AudioMediaFormat(MediaFormatAudioMimeType mimeType,
159             int channel, int sampleRate, int bit, int bitRate, MediaFormatAacType aacType, int bitDepth, IList<MediaFormatAudioChannelPosition> audioChannelMap)
160             : base(MediaFormatType.Audio)
161         {
162             ValidationUtil.ValidateEnum(typeof(MediaFormatAudioMimeType), mimeType, nameof(mimeType));
163
164             if (channel < 0)
165             {
166                 throw new ArgumentOutOfRangeException(nameof(channel), channel,
167                     "Channel value can't be negative.");
168             }
169             if (sampleRate < 0)
170             {
171                 throw new ArgumentOutOfRangeException(nameof(sampleRate), sampleRate,
172                     "Sample rate value can't be negative.");
173             }
174             if (bit < 0)
175             {
176                 throw new ArgumentOutOfRangeException(nameof(bit), bit,
177                     "Bit value can't be negative.");
178             }
179             if (bitRate < 0)
180             {
181                 throw new ArgumentOutOfRangeException(nameof(bitRate), bitRate,
182                     "Bit rate value can't be negative.");
183             }
184             if (bitDepth < 0)
185             {
186                 throw new ArgumentOutOfRangeException(nameof(bitDepth), bitDepth,
187                     "Bit depth value can't be negative.");
188             }
189
190             ValidationUtil.ValidateEnum(typeof(MediaFormatAacType), aacType, nameof(aacType));
191
192             if (!IsAacSupportedMimeType(mimeType) && aacType != MediaFormatAacType.None)
193             {
194                 throw new ArgumentException("Aac is supported only with aac mime types.");
195             }
196
197             MimeType = mimeType;
198             AacType = aacType;
199             Channel = channel;
200             SampleRate = sampleRate;
201             Bit = bit;
202             BitRate = bitRate;
203             BitDepth = bitDepth;
204
205             if (audioChannelMap != null)
206             {
207                 audioChannelMap = audioChannelMap.Distinct().OrderBy(p => p).ToList();
208
209                 ValidateAudioChannelMap(audioChannelMap);
210
211                 AudioChannelMap = new ReadOnlyCollection<MediaFormatAudioChannelPosition>(audioChannelMap);
212             }
213         }
214
215         private void ValidateAudioChannelMap(IList<MediaFormatAudioChannelPosition> audioChannelMap)
216         {
217             if (audioChannelMap.Contains(MediaFormatAudioChannelPosition.Invaild))
218             {
219                 throw new ArgumentException("Invalid channel position.", nameof(audioChannelMap));
220             }
221
222             if ((audioChannelMap.Contains(MediaFormatAudioChannelPosition.Mono) && audioChannelMap.Count > 1) ||
223                 (audioChannelMap.Contains(MediaFormatAudioChannelPosition.None) && audioChannelMap.Count > 1))
224             {
225                 throw new ArgumentException($"Mono and None can not be set with another channel position.",
226                     nameof(audioChannelMap));
227             }
228
229             if (audioChannelMap.Contains(MediaFormatAudioChannelPosition.None))
230             {
231                 if (Channel <= 0)
232                 {
233                     throw new ArgumentException($"Channel should be greater than 0 in {MediaFormatAudioChannelPosition.None}.",
234                         nameof(audioChannelMap));
235                 }
236             }
237             else
238             {
239                 if (audioChannelMap.Count != Channel)
240                 {
241                     throw new ArgumentException("Channel should be the same with number of audioChannelMap.",
242                         nameof(audioChannelMap));
243                 }
244             }
245         }
246
247         /// <summary>
248         /// Initializes a new instance of the AudioMediaFormat class from a native handle.
249         /// </summary>
250         /// <param name="handle">A native handle.</param>
251         internal AudioMediaFormat(IntPtr handle)
252             : base(MediaFormatType.Audio)
253         {
254             Debug.Assert(handle != IntPtr.Zero, "The handle is invalid!");
255
256             GetInfo(handle, out var mimeType, out var channel, out var sampleRate, out var bit, out var bitRate);
257
258             MimeType = mimeType;
259             Channel = channel;
260             SampleRate = sampleRate;
261             Bit = bit;
262             BitRate = bitRate;
263             AacType = IsAacSupportedMimeType(mimeType) ? GetAacType(handle) : MediaFormatAacType.None;
264             AudioChannelMap = Channel == 0 ? null : GetAudioChannelMap(handle);
265         }
266
267         private static ReadOnlyCollection<MediaFormatAudioChannelPosition> GetAudioChannelMap(IntPtr handle)
268         {
269             var ret = Native.GetAudioChannelMask(handle, out ulong mask);
270             MultimediaDebug.AssertNoError(ret);
271
272             var positions = new MediaFormatAudioChannelPosition[Enum.GetNames(typeof(MediaFormatAudioChannelPosition)).Length];
273
274             ret = Native.GetChannelPositionFromMask(handle, mask, ref positions);
275             MultimediaDebug.AssertNoError(ret);
276
277             return positions == null ? null :
278                 new ReadOnlyCollection<MediaFormatAudioChannelPosition>(positions.Distinct().OrderBy(p => p).ToList());
279         }
280
281         /// <summary>
282         /// Returns an indication whether a specified mime type is an AAC type.
283         /// </summary>
284         /// <param name="mimeType">A mime type.</param>
285         private static bool IsAacSupportedMimeType(MediaFormatAudioMimeType mimeType)
286         {
287             return mimeType == MediaFormatAudioMimeType.AacLC ||
288                 mimeType == MediaFormatAudioMimeType.AacHE ||
289                 mimeType == MediaFormatAudioMimeType.AacHEPS;
290         }
291
292         /// <summary>
293         /// Retrieves audio properties of the media format from a native handle.
294         /// </summary>
295         /// <param name="handle">A native handle that the properties are retrieved from.</param>
296         /// <param name="mimeType">An out parameter for the mime type.</param>
297         /// <param name="channel">An out parameter for the channel.</param>
298         /// <param name="sampleRate">An out parameter for the sample rate.</param>
299         /// <param name="bit">An out parameter for the bit.</param>
300         /// <param name="bitRate">An out parameter for the bit rate.</param>
301         private static void GetInfo(IntPtr handle, out MediaFormatAudioMimeType mimeType,
302             out int channel, out int sampleRate, out int bit, out int bitRate)
303         {
304             Debug.Assert(handle != IntPtr.Zero, "The handle is invalid!");
305
306             int ret = Native.GetAudioInfo(handle,
307                 out mimeType, out channel, out sampleRate, out bit, out bitRate);
308
309             MultimediaDebug.AssertNoError(ret);
310
311             Debug.Assert(Enum.IsDefined(typeof(MediaFormatAudioMimeType), mimeType),
312                 "Invalid audio mime type!");
313         }
314
315         /// <summary>
316         /// Retrieves the AAC type value from a native handle.
317         /// </summary>
318         /// <param name="handle">A native handle that the properties are retrieved from.</param>
319         private static MediaFormatAacType GetAacType(IntPtr handle)
320         {
321             Debug.Assert(handle != IntPtr.Zero, "The handle is invalid!");
322
323             int ret = Native.GetAudioAacType(handle, out var aacType);
324
325             MultimediaDebug.AssertNoError(ret);
326
327             Debug.Assert(Enum.IsDefined(typeof(MediaFormatAacType), aacType), "Invalid aac type!");
328
329             return aacType;
330         }
331
332         internal override void AsNativeHandle(IntPtr handle)
333         {
334             Debug.Assert(Type == MediaFormatType.Audio);
335
336             int ret = Native.SetAudioMimeType(handle, MimeType);
337             MultimediaDebug.AssertNoError(ret);
338
339             ret = Native.SetAudioChannel(handle, Channel);
340             MultimediaDebug.AssertNoError(ret);
341
342             ret = Native.SetAudioSampleRate(handle, SampleRate);
343             MultimediaDebug.AssertNoError(ret);
344
345             ret = Native.SetAudioBit(handle, Bit);
346             MultimediaDebug.AssertNoError(ret);
347
348             ret = Native.SetAudioAverageBps(handle, BitRate);
349             MultimediaDebug.AssertNoError(ret);
350
351             ret = Native.SetAudioAacType(handle, AacType);
352             MultimediaDebug.AssertNoError(ret);
353
354             if (AudioChannelMap != null)
355             {
356                 ret = Native.SetAudioChannelMask(handle, GetAudioChannelMask(handle, AudioChannelMap));
357                 MultimediaDebug.AssertNoError(ret);
358             }
359         }
360
361         private static ulong GetAudioChannelMask(IntPtr handle, IList<MediaFormatAudioChannelPosition> audioChannelMap)
362         {
363             int ret = Native.GetMaskFromChannelPosition(handle, audioChannelMap.ToArray(),
364                 out ulong mask);
365             MultimediaDebug.AssertNoError(ret);
366
367             return mask;
368         }
369
370         /// <summary>
371         /// Gets the mime type of the current format.
372         /// </summary>
373         /// <since_tizen> 3 </since_tizen>
374         public MediaFormatAudioMimeType MimeType { get; }
375
376         /// <summary>
377         /// Gets the channel value of the current format.
378         /// </summary>
379         /// <since_tizen> 3 </since_tizen>
380         public int Channel { get; }
381
382         /// <summary>
383         /// Gets or sets the list of channel position value of PCM audio format.
384         /// </summary>
385         /// <remarks>
386         /// The channel mask specifies the mapping of channels to speakers.
387         /// default value is 0.
388         /// </remarks>
389         /// <seealso cref="Channel"/>
390         /// <seealso cref="MediaFormatAudioChannelPosition"/>
391         /// <since_tizen> 6 </since_tizen>
392         public ReadOnlyCollection<MediaFormatAudioChannelPosition> AudioChannelMap { get; }
393
394         /// <summary>
395         /// Gets the sample rate value of the current format.
396         /// </summary>
397         /// <since_tizen> 3 </since_tizen>
398         public int SampleRate { get; }
399
400         /// <summary>
401         /// Gets the bit value of the current format.
402         /// </summary>
403         /// <since_tizen> 3 </since_tizen>
404         public int Bit { get; }
405
406         /// <summary>
407         /// Gets the bit rate value of the current format.
408         /// </summary>
409         /// <since_tizen> 3 </since_tizen>
410         public int BitRate { get; }
411
412         /// <summary>
413         /// Gets the bit depth value of the current format.
414         /// </summary>
415         /// <since_tizen> 6 </since_tizen>
416         public int BitDepth { get; }
417
418         /// <summary>
419         /// Gets the AAC type of the current format.
420         /// </summary>
421         /// <since_tizen> 3 </since_tizen>
422         public MediaFormatAacType AacType { get; }
423
424         /// <summary>
425         /// Returns a string that represents the current object.
426         /// </summary>
427         /// <returns>A string that represents the current object.</returns>
428         /// <since_tizen> 3 </since_tizen>
429         public override string ToString()
430         {
431             var toString = $@"MimeType={ MimeType.ToString() }, Channel={ Channel.ToString() }, SampleRate={ SampleRate },
432                 Bit={ Bit.ToString() }, BitRate={ BitRate.ToString() }, BitDepth={ BitDepth.ToString() }, AacType={ AacType.ToString()}";
433
434             if (AudioChannelMap != null)
435             {
436                 toString += ", AudioChannelMap=" + $"{string.Join(",", AudioChannelMap)}";
437             }
438
439             return toString;
440         }
441
442         /// <summary>
443         /// Compares an object to an instance of <see cref="AudioMediaFormat"/> for equality.
444         /// </summary>
445         /// <param name="obj">A <see cref="Object"/> to compare.</param>
446         /// <returns>true if the formats are equal; otherwise, false.</returns>
447         /// <since_tizen> 3 </since_tizen>
448         public override bool Equals(object obj)
449         {
450             var rhs = obj as AudioMediaFormat;
451             if (rhs == null)
452             {
453                 return false;
454             }
455
456             var mapCompare = true;
457             // We don't care the case of both properties are null.
458             if (AudioChannelMap != null && rhs.AudioChannelMap != null)
459             {
460                 for (int i = 0; i < AudioChannelMap.Count; i++)
461                 {
462                     mapCompare = AudioChannelMap[i].Equals(rhs.AudioChannelMap[i]);
463                 }
464             }
465             else if ((AudioChannelMap == null && rhs.AudioChannelMap != null) ||
466                 (AudioChannelMap != null && rhs.AudioChannelMap == null))
467             {
468                 mapCompare = false;
469             }
470
471             return MimeType == rhs.MimeType && Channel == rhs.Channel && SampleRate == rhs.SampleRate &&
472                 Bit == rhs.Bit && BitRate == rhs.BitRate && BitDepth == rhs.BitDepth && AacType == rhs.AacType && mapCompare;
473         }
474
475         /// <summary>
476         /// Gets the hash code for this instance of <see cref="AudioMediaFormat"/>.
477         /// </summary>
478         /// <returns>The hash code for this instance of <see cref="AudioMediaFormat"/>.</returns>
479         /// <since_tizen> 3 </since_tizen>
480         public override int GetHashCode()
481             => new { MimeType, Channel, SampleRate, Bit, BitRate, BitDepth, AacType, AudioChannelMap }.GetHashCode();
482     }
483 }