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