[WebRTC] Add initial code (#2977)
[platform/core/csapi/tizenfx.git] / src / Tizen.Multimedia.Remoting / WebRTC / MediaPacketSource.cs
1 /*
2  * Copyright (c) 2021 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.Collections.Generic;
19 using System.Diagnostics;
20 using System.Linq;
21 using static Interop;
22
23 namespace Tizen.Multimedia.Remoting
24 {
25     /// <summary>
26     /// Represents a media packet source.
27     /// </summary>
28     /// <seealso cref="WebRTC.AddSource"/>
29     /// <seealso cref="WebRTC.AddSources"/>
30     /// <since_tizen> 9 </since_tizen>
31     public sealed class MediaPacketSource : MediaSource
32     {
33         private readonly MediaFormat _audioMediaFormat;
34         private readonly MediaFormat _videoMediaFormat;
35         private static List<MediaFormatAudioMimeType> _supportedAudioFormats;
36         private static List<MediaFormatVideoMimeType> _supportedVideoFormats;
37
38         /// <summary>
39         /// Gets all supported audio types.
40         /// </summary>
41         /// <since_tizen> 9 </since_tizen>
42         public static IEnumerable<MediaFormatAudioMimeType> SupportedAudioTypes
43         {
44             get
45             {
46                 GetSupportedTypes();
47                 return _supportedAudioFormats.AsReadOnly();
48             }
49         }
50
51         /// <summary>
52         /// Gets all supported video types.
53         /// </summary>
54         /// <since_tizen> 9 </since_tizen>
55         public static IEnumerable<MediaFormatVideoMimeType> SupportedVideoTypes
56         {
57             get
58             {
59                 GetSupportedTypes();
60                 return _supportedVideoFormats.AsReadOnly();
61             }
62         }
63
64         private static void GetSupportedTypes()
65         {
66             if (_supportedAudioFormats != null || _supportedVideoFormats != null)
67             {
68                 return;
69             }
70
71             // Currentely, supported formats are fixed in native fw but I'll keep this for future use.
72             _supportedAudioFormats = new List<MediaFormatAudioMimeType>()
73             {
74                 MediaFormatAudioMimeType.Vorbis,
75                 MediaFormatAudioMimeType.Opus,
76                 MediaFormatAudioMimeType.Pcm
77             };
78             _supportedVideoFormats = new List<MediaFormatVideoMimeType>()
79             {
80                 MediaFormatVideoMimeType.H264SP,
81                 MediaFormatVideoMimeType.H264MP,
82                 MediaFormatVideoMimeType.H264HP,
83                 MediaFormatVideoMimeType.MJpeg,
84                 MediaFormatVideoMimeType.Vp8,
85                 MediaFormatVideoMimeType.Vp9,
86                 MediaFormatVideoMimeType.I420,
87                 MediaFormatVideoMimeType.NV12
88             };
89         }
90
91         private MediaPacketSourceConfiguration CreateAudioConfiguration(AudioMediaFormat format)
92         {
93             if (format == null)
94             {
95                 return null;
96             }
97
98             if (!SupportedAudioTypes.Contains<MediaFormatAudioMimeType>(format.MimeType))
99             {
100                 throw new ArgumentException($"The audio format is not supported, Type : {format.MimeType}.");
101             }
102
103             return new MediaPacketSourceConfiguration(this);
104         }
105
106         private MediaPacketSourceConfiguration CreateVideoConfiguration(VideoMediaFormat format)
107         {
108             if (format == null)
109             {
110                 return null;
111             }
112
113             if (!SupportedVideoTypes.Contains(format.MimeType))
114             {
115                 throw new ArgumentException($"The video format is not supported, Type : {format.MimeType}.");
116             }
117             return new MediaPacketSourceConfiguration(this);
118         }
119
120         /// <summary>
121         /// Initializes a new instance of the MediaPacketSource class with the specified <see cref="AudioMediaFormat"/>.
122         /// </summary>
123         /// <param name="audioMediaFormat">The <see cref="AudioMediaFormat"/> for this source.</param>
124         /// <exception cref="ArgumentNullException"><paramref name="audioMediaFormat"/> is null.</exception>
125         /// <exception cref="ArgumentException"><paramref name="audioMediaFormat"/> is not supported.</exception>
126         /// <seealso cref="SupportedAudioTypes"/>
127         /// <since_tizen> 9 </since_tizen>
128         public MediaPacketSource(AudioMediaFormat audioMediaFormat) : base(MediaType.Audio)
129         {
130             _audioMediaFormat = audioMediaFormat ?? throw new ArgumentNullException(nameof(audioMediaFormat));
131             AudioConfiguration = CreateAudioConfiguration(audioMediaFormat);
132         }
133
134         /// <summary>
135         /// Initializes a new instance of the MediaPacketSource class with the specified <see cref="VideoMediaFormat"/>.
136         /// </summary>
137         /// <param name="videoMediaFormat">The <see cref="VideoMediaFormat"/> for this source.</param>
138         /// <exception cref="ArgumentNullException"><paramref name="videoMediaFormat"/> is null.</exception>
139         /// <exception cref="ArgumentException"><paramref name="videoMediaFormat"/> is not supported.</exception>
140         /// <seealso cref="SupportedVideoTypes"/>
141         /// <since_tizen> 9 </since_tizen>
142         public MediaPacketSource(VideoMediaFormat videoMediaFormat) : base(MediaType.Video)
143         {
144             _videoMediaFormat = videoMediaFormat ?? throw new ArgumentNullException(nameof(videoMediaFormat));
145             VideoConfiguration = CreateVideoConfiguration(videoMediaFormat);
146         }
147
148         /// <summary>
149         /// Gets the audio configuration, or null if no AudioMediaFormat is specified in the constructor.
150         /// </summary>
151         /// <since_tizen> 9 </since_tizen>
152         public MediaPacketSourceConfiguration AudioConfiguration { get; }
153
154         /// <summary>
155         /// Gets the video configuration, or null if no VideoMediaFormat is specified in the constructor.
156         /// </summary>
157         /// <since_tizen> 9 </since_tizen>
158         public MediaPacketSourceConfiguration VideoConfiguration { get; }
159
160         /// <summary>
161         /// Pushes elementary stream to decode audio or video.
162         /// </summary>
163         /// <remarks>
164         /// This source must be set as a source to a WebRTC and the WebRTC must be in the
165         /// <see cref="WebRTCState.Negotiating"/> or <see cref="WebRTCState.Playing"/> state
166         /// </remarks>
167         /// <param name="packet">The <see cref="MediaPacket"/> to decode.</param>
168         /// <exception cref="InvalidOperationException">
169         ///     This source is not set as a source to a WebRTC.<br/>
170         ///     -or-<br/>
171         ///     The WebRTC is not in the valid state.
172         /// </exception>
173         /// <exception cref="ArgumentNullException"><paramref name="packet"/> is null.</exception>
174         /// <exception cref="ObjectDisposedException"><paramref name="packet"/> has been disposed.</exception>
175         /// <exception cref="ArgumentException">
176         ///     <paramref name="packet"/> is neither video nor audio type.<br/>
177         ///     -or-<br/>
178         ///     The format of packet is not matched with the specified format in the constructor.
179         /// </exception>
180         /// <seealso cref="WebRTC.AddSource"/>
181         /// <seealso cref="WebRTC.AddSources"/>
182         /// <seealso cref="MediaPacket"/>
183         /// <since_tizen> 9 </since_tizen>
184         public void Push(MediaPacket packet)
185         {
186             if (WebRtc == null)
187             {
188                 Log.Error(WebRTCLog.Tag, "The source is not set as a source to a WebRTC yet.");
189                 throw new InvalidOperationException("The source is not set as a source to a WebRTC yet.");
190             }
191
192             if (packet == null)
193             {
194                 Log.Error(WebRTCLog.Tag, "packet is null");
195                 throw new ArgumentNullException(nameof(packet));
196             }
197
198             if (packet.IsDisposed)
199             {
200                 Log.Error(WebRTCLog.Tag, "packet is disposed");
201                 throw new ObjectDisposedException(nameof(packet));
202             }
203
204             if (packet.Format.Type == MediaFormatType.Text || packet.Format.Type == MediaFormatType.Container)
205             {
206                 Log.Error(WebRTCLog.Tag, "The format of the packet is invalid : " + packet.Format.Type);
207                 throw new ArgumentException($"The format of the packet is invalid : {packet.Format.Type}.");
208             }
209
210             if (!packet.Format.Equals(_audioMediaFormat) && !packet.Format.Equals(_videoMediaFormat))
211             {
212                 Log.Error(WebRTCLog.Tag, "The format of the packet is invalid : Unmatched format.");
213                 throw new ArgumentException("The format of the packet is invalid : Unmatched format.");
214             }
215
216             if (packet.Format.Type == MediaFormatType.Video && _videoMediaFormat == null)
217             {
218                 Log.Error(WebRTCLog.Tag, "Video is not configured with the current source.");
219                 throw new ArgumentException("Video is not configured with the current source.");
220             }
221
222             if (packet.Format.Type == MediaFormatType.Audio && _audioMediaFormat == null)
223             {
224                 Log.Error(WebRTCLog.Tag, "Audio is not configured with the current source.");
225                 throw new ArgumentException("Audio is not configured with the current source.");
226             }
227
228             WebRtc.ValidateWebRTCState(WebRTCState.Negotiating, WebRTCState.Playing);
229
230             NativeWebRTC.PushMediaPacket(WebRtc.Handle, SourceId.Value, packet.GetHandle()).
231                 ThrowIfFailed("Failed to push the packet to the WebRTC");
232         }
233
234         private void SetMediaStreamInfo(MediaFormat mediaFormat)
235         {
236             if (mediaFormat == null)
237             {
238                 Log.Warn(WebRTCLog.Tag, "invalid media format");
239             }
240             else
241             {
242                 IntPtr ptr = IntPtr.Zero;
243
244                 try
245                 {
246                     ptr = mediaFormat.AsNativeHandle();
247                     NativeWebRTC.SetMediaPacketSourceInfo(WebRtc.Handle, SourceId.Value, ptr).
248                         ThrowIfFailed("Failed to set the media stream info");
249                 }
250                 finally
251                 {
252                     MediaFormat.ReleaseNativeHandle(ptr);
253                 }
254             }
255         }
256
257         internal override void OnAttached(WebRTC webRtc)
258         {
259             Debug.Assert(webRtc != null);
260
261             if (WebRtc != null)
262             {
263                 Log.Error(WebRTCLog.Tag, "The source is has already been assigned to another WebRTC.");
264                 throw new InvalidOperationException("The source is has already been assigned to another WebRTC.");
265             }
266
267             NativeWebRTC.AddMediaSource(webRtc.Handle, MediaSourceType.MediaPacket, out uint sourceId).
268                 ThrowIfFailed("Failed to add MediaPacketSource.");
269
270             WebRtc = webRtc;
271             SourceId = sourceId;
272
273             AudioConfiguration?.OnWebRTCSet();
274             VideoConfiguration?.OnWebRTCSet();
275
276             SetMediaStreamInfo(_audioMediaFormat);
277             SetMediaStreamInfo(_videoMediaFormat);
278         }
279
280         internal override void OnDetached(WebRTC webRtc)
281         {
282             NativeWebRTC.RemoveMediaSource(webRtc.Handle, SourceId.Value).
283                 ThrowIfFailed("Failed to remove MediaPacketSource.");
284
285             AudioConfiguration?.OnWebRTCUnset();
286             VideoConfiguration?.OnWebRTCUnset();
287
288             WebRtc = null;
289         }
290     }
291 }