Release 4.0.0-preview1-00051
[platform/core/csapi/tizenfx.git] / src / Tizen.Multimedia.MediaPlayer / Player / MediaStreamSource.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.Diagnostics;
19 using System.Linq;
20 using static Interop;
21
22 namespace Tizen.Multimedia
23 {
24
25     /// <summary>
26     /// Provides the ability to push packets as the source of <see cref="Player"/>
27     /// </summary>
28     /// <remarks>The source must be set as a source to a player before pushing.</remarks>
29     /// <seealso cref="Player.SetSource(MediaSource)"/>
30     public sealed class MediaStreamSource : MediaSource
31     {
32         private readonly MediaFormat _audioMediaFormat;
33         private readonly MediaFormat _videoMediaFormat;
34
35         /// <summary>
36         /// Gets all supported audio types.
37         /// </summary>
38         public static IEnumerable<MediaFormatAudioMimeType> SupportedAudioTypes
39         {
40             get
41             {
42                 yield return MediaFormatAudioMimeType.Aac;
43             }
44         }
45
46         /// <summary>
47         /// Gets all supported video types.
48         /// </summary>
49         public static IEnumerable<MediaFormatVideoMimeType> SupportedVideoTypes
50         {
51             get
52             {
53                 yield return MediaFormatVideoMimeType.H264SP;
54             }
55         }
56
57         private Player _player;
58
59         private MediaStreamConfiguration CreateAudioConfiguration(AudioMediaFormat format)
60         {
61             if( format == null )
62             {
63                 return null;
64             }
65
66             if (!SupportedAudioTypes.Contains(format.MimeType))
67             {
68                 Log.Error(PlayerLog.Tag, "The audio format is not supported : " + format.MimeType);
69                 throw new ArgumentException($"The audio format is not supported, Type : {format.MimeType}.");
70             }
71
72             return new MediaStreamConfiguration(this, StreamType.Audio);
73         }
74
75         private MediaStreamConfiguration CreateVideoConfiguration(VideoMediaFormat format)
76         {
77             if (format == null)
78             {
79                 return null;
80             }
81
82             if (!SupportedVideoTypes.Contains(format.MimeType))
83             {
84                 Log.Error(PlayerLog.Tag, "The video format is not supported : " + format.MimeType);
85                 throw new ArgumentException($"The video format is not supported, Type : {format.MimeType}.");
86             }
87
88             return new MediaStreamConfiguration(this, StreamType.Video);
89         }
90
91
92         /// <summary>
93         /// Initialize a new instance of the MediaStreamSource class
94         /// with the specified <see cref="AudioMediaFormat"/> and <see cref="VideoMediaFormat"/>.
95         /// </summary>
96         /// <param name="audioMediaFormat">The <see cref="AudioMediaFormat"/> for this source.</param>
97         /// <param name="videoMediaFormat">The <see cref="VideoMediaFormat"/> for this source.</param>
98         /// <remarks>AAC and H.264 are supported.</remarks>
99         /// <exception cref="ArgumentNullException">Both <paramref name="audioMediaFormat"/> and <paramref name="videoMediaFormat"/> are null.</exception>
100         /// <exception cref="ArgumentException">
101         ///     <paramref name="audioMediaFormat"/> is not supported.\n
102         ///     -or-\n
103         ///     <paramref name="videoMediaFormat"/> is not supported.\n
104         /// </exception>
105         /// <seealso cref="SupportedAudioTypes"/>
106         /// <seealso cref="SupportedVideoTypes"/>
107         public MediaStreamSource(AudioMediaFormat audioMediaFormat, VideoMediaFormat videoMediaFormat)
108         {
109             if (audioMediaFormat == null && videoMediaFormat == null)
110             {
111                 throw new ArgumentNullException(nameof(audioMediaFormat) + " and " + nameof(videoMediaFormat));
112             }
113
114             _audioMediaFormat = audioMediaFormat;
115             _videoMediaFormat = videoMediaFormat;
116
117             AudioConfiguration = CreateAudioConfiguration(audioMediaFormat);
118             VideoConfiguration = CreateVideoConfiguration(videoMediaFormat);
119         }
120
121         /// <summary>
122         /// Initialize a new instance of the MediaStreamSource class with the specified <see cref="AudioMediaFormat"/>.
123         /// </summary>
124         /// <param name="audioMediaFormat">The <see cref="AudioMediaFormat"/> for this source.</param>
125         /// <remarks>AAC is supported.</remarks>
126         /// <exception cref="ArgumentNullException"><paramref name="audioMediaFormat"/> is null.</exception>
127         /// <exception cref="ArgumentException"><paramref name="audioMediaFormat"/> is not supported.</exception>
128         /// <seealso cref="SupportedAudioTypes"/>
129         public MediaStreamSource(AudioMediaFormat audioMediaFormat)
130         {
131             if (audioMediaFormat == null)
132             {
133                 throw new ArgumentNullException(nameof(audioMediaFormat));
134             }
135
136             _audioMediaFormat = audioMediaFormat;
137
138             AudioConfiguration = CreateAudioConfiguration(audioMediaFormat);
139         }
140         /// <summary>
141         /// Initialize a new instance of the MediaStreamSource class with the specified <see cref="VideoMediaFormat"/>.
142         /// </summary>
143         /// <remarks>H.264 can is supported.</remarks>
144         /// <param name="videoMediaFormat">The <see cref="VideoMediaFormat"/> for this source.</param>
145         /// <exception cref="ArgumentNullException"><paramref name="videoMediaFormat"/> is null.</exception>
146         /// <exception cref="ArgumentException"><paramref name="videoMediaFormat"/> is not supported.</exception>
147         /// <seealso cref="SupportedVideoTypes"/>
148         public MediaStreamSource(VideoMediaFormat videoMediaFormat)
149         {
150             if (videoMediaFormat == null)
151             {
152                 throw new ArgumentNullException(nameof(videoMediaFormat));
153             }
154
155             _videoMediaFormat = videoMediaFormat;
156
157             VideoConfiguration = CreateVideoConfiguration(videoMediaFormat);
158         }
159
160         /// <summary>
161         /// Gets the audio configuration or null if no AudioMediaFormat is specified in the constructor.
162         /// </summary>
163         public MediaStreamConfiguration AudioConfiguration { get; }
164
165         /// <summary>
166         /// Gets the video configuration or null if no VideoMediaFormat is specified in the constructor.
167         /// </summary>
168         public MediaStreamConfiguration VideoConfiguration { get; }
169
170         /// <summary>
171         /// Pushes elementary stream to decode audio or video.
172         /// </summary>
173         /// <remarks>This source must be set as a source to a player and the player must be in the <see cref="PlayerState.Ready"/>, <see cref="PlayerState.Playing"/> or <see cref="PlayerState.Paused"/> state.</remarks>
174         /// <param name="packet">The <see cref="MediaPacket"/> to decode.</param>
175         /// <exception cref="InvalidOperationException">
176         ///     This source is not set as a source to a player.\n
177         ///     -or-\n
178         ///     The player is not in the valid state.
179         /// </exception>
180         /// <exception cref="ArgumentNullException">packet is null.</exception>
181         /// <exception cref="ObjectDisposedException">packet has been disposed.</exception>
182         /// <exception cref="ArgumentException">
183         ///     <paramref name="packet"/> is neither video nor audio type.\n
184         ///     -or-\n
185         ///     The format of packet is not matched with the specified format in the constructor.
186         /// </exception>
187         /// <exception cref="NoBufferSpaceException">the internal buffer reaches limits.</exception>
188         /// <seealso cref="Player.SetSource(MediaSource)"/>
189         /// <seealso cref="MediaStreamConfiguration.BufferMaxSize"/>
190         /// <seealso cref="MediaPacket"/>
191         public void Push(MediaPacket packet)
192         {
193             if (_player == null)
194             {
195                 Log.Error(PlayerLog.Tag, "The source is not set as a source to a player yet.");
196                 throw new InvalidOperationException("The source is not set as a source to a player yet.");
197             }
198             if (packet == null)
199             {
200                 Log.Error(PlayerLog.Tag, "packet is null");
201                 throw new ArgumentNullException(nameof(packet));
202             }
203             if (packet.IsDisposed)
204             {
205                 Log.Error(PlayerLog.Tag, "packet is disposed");
206                 throw new ObjectDisposedException(nameof(packet));
207             }
208
209             if (packet.Format.Type == MediaFormatType.Text || packet.Format.Type == MediaFormatType.Container)
210             {
211                 Log.Error(PlayerLog.Tag, "The format of the packet is invalid : " + packet.Format.Type);
212                 throw new ArgumentException($"The format of the packet is invalid : { packet.Format.Type }.");
213             }
214
215             if (!packet.Format.Equals(_audioMediaFormat) && !packet.Format.Equals(_videoMediaFormat))
216             {
217                 Log.Error(PlayerLog.Tag, "The format of the packet is invalid : Unmatched format.");
218                 throw new ArgumentException($"The format of the packet is invalid : Unmatched format.");
219             }
220
221             if (packet.Format.Type == MediaFormatType.Video && _videoMediaFormat == null)
222             {
223                 Log.Error(PlayerLog.Tag, "Video is not configured with the current source.");
224                 throw new ArgumentException("Video is not configured with the current source.");
225             }
226             if (packet.Format.Type == MediaFormatType.Audio && _audioMediaFormat == null)
227             {
228                 Log.Error(PlayerLog.Tag, "Audio is not configured with the current source.");
229                 throw new ArgumentException("Audio is not configured with the current source.");
230             }
231
232             _player.ValidatePlayerState(PlayerState.Paused, PlayerState.Playing, PlayerState.Ready);
233
234             NativePlayer.PushMediaStream(_player.Handle, packet.GetHandle()).
235                 ThrowIfFailed("Failed to push the packet to the player");
236         }
237
238         private void SetMediaStreamInfo(StreamType streamType, MediaFormat mediaFormat)
239         {
240             if (mediaFormat == null)
241             {
242                 Log.Error(PlayerLog.Tag, "invalid media format");
243                 return;
244             }
245
246             IntPtr ptr = IntPtr.Zero;
247
248             try
249             {
250                 ptr = mediaFormat.AsNativeHandle();
251                 NativePlayer.SetMediaStreamInfo(_player.Handle, (int)streamType, ptr).
252                     ThrowIfFailed("Failed to set the media stream info");
253             }
254             finally
255             {
256                 MediaFormat.ReleaseNativeHandle(ptr);
257             }
258         }
259
260         internal override void OnAttached(Player player)
261         {
262             Debug.Assert(player != null);
263
264             if (_player != null)
265             {
266                 Log.Error(PlayerLog.Tag, "The source is has already been assigned to another player.");
267                 throw new InvalidOperationException("The source is has already been assigned to another player.");
268             }
269
270             AudioConfiguration?.OnPlayerSet(player);
271             VideoConfiguration?.OnPlayerSet(player);
272
273             _player = player;
274
275             SetMediaStreamInfo(StreamType.Audio, _audioMediaFormat);
276             SetMediaStreamInfo(StreamType.Video, _videoMediaFormat);
277         }
278
279         internal override void OnDetached(Player player)
280         {
281             base.OnDetached(player);
282
283             AudioConfiguration?.OnPlayerUnset(player);
284             VideoConfiguration?.OnPlayerUnset(player);
285
286             _player = null;
287         }
288
289         /// <summary>
290         /// Gets the <see cref="Player"/> that this source is assigned to as a source or null if this source is not assigned.
291         /// </summary>
292         /// <seealso cref="Player.SetSource(MediaSource)"/>
293         public Player Player => _player;
294
295     }
296 }