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