[MediaPlayer] Added ErrorHandler registration methods for internal use. (#33)
[platform/core/csapi/tizenfx.git] / src / Tizen.Multimedia.MediaPlayer / Player / Player.Events.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.Threading.Tasks;
18 using System.Runtime.InteropServices;
19 using System.Diagnostics;
20 using System.IO;
21 using System.Threading;
22 using static Interop;
23
24 namespace Tizen.Multimedia
25 {
26     public partial class Player
27     {
28         /// <summary>
29         /// Occurs when the playback of a media is finished.
30         /// </summary>
31         /// <since_tizen> 3 </since_tizen>
32         public event EventHandler<EventArgs> PlaybackCompleted;
33         private NativePlayer.PlaybackCompletedCallback _playbackCompletedCallback;
34
35         /// <summary>
36         /// Occurs when the playback of a media is interrupted.
37         /// </summary>
38         /// <remarks>
39         /// If the reason is <see cref="PlaybackInterruptionReason.ResourceConflict"/>,
40         /// the player state will be one of <see cref="PlayerState.Idle"/>, <see cref="PlayerState.Ready"/>,
41         /// or <see cref="PlayerState.Paused"/>.
42         /// </remarks>
43         /// <seealso cref="Player.State"/>
44         /// <since_tizen> 3 </since_tizen>
45         public event EventHandler<PlaybackInterruptedEventArgs> PlaybackInterrupted;
46         private NativePlayer.PlaybackInterruptedCallback _playbackInterruptedCallback;
47
48         /// <summary>
49         /// Occurs when any error occurs.
50         /// </summary>
51         /// <remarks>The event handler will be executed on an internal thread.</remarks>
52         /// <since_tizen> 3 </since_tizen>
53         public event EventHandler<PlayerErrorOccurredEventArgs> ErrorOccurred;
54         private NativePlayer.PlaybackErrorCallback _playbackErrorCallback;
55
56         /// <summary>
57         /// Occurs when the video stream is changed.
58         /// </summary>
59         /// <remarks>The event handler will be executed on an internal thread.</remarks>
60         /// <since_tizen> 3 </since_tizen>
61         public event EventHandler<VideoStreamChangedEventArgs> VideoStreamChanged;
62         private NativePlayer.VideoStreamChangedCallback _videoStreamChangedCallback;
63
64         /// <summary>
65         /// Occurs when the subtitle is updated.
66         /// </summary>
67         /// <remarks>The event handler will be executed on an internal thread.</remarks>
68         /// <since_tizen> 3 </since_tizen>
69         public event EventHandler<SubtitleUpdatedEventArgs> SubtitleUpdated;
70         private NativePlayer.SubtitleUpdatedCallback _subtitleUpdatedCallback;
71
72         /// <summary>
73         /// Occurs when there is a change in the buffering status of streaming.
74         /// </summary>
75         /// <since_tizen> 3 </since_tizen>
76         public event EventHandler<BufferingProgressChangedEventArgs> BufferingProgressChanged;
77         private NativePlayer.BufferingProgressCallback _bufferingProgressCallback;
78
79         internal event EventHandler<MediaStreamBufferStatusChangedEventArgs> MediaStreamAudioBufferStatusChanged;
80         private NativePlayer.MediaStreamBufferStatusCallback _mediaStreamAudioBufferStatusChangedCallback;
81
82         internal event EventHandler<MediaStreamBufferStatusChangedEventArgs> MediaStreamVideoBufferStatusChanged;
83         private NativePlayer.MediaStreamBufferStatusCallback _mediaStreamVideoBufferStatusChangedCallback;
84
85         internal event EventHandler<MediaStreamSeekingOccurredEventArgs> MediaStreamAudioSeekingOccurred;
86         private NativePlayer.MediaStreamSeekCallback _mediaStreamAudioSeekCallback;
87
88         internal event EventHandler<MediaStreamSeekingOccurredEventArgs> MediaStreamVideoSeekingOccurred;
89         private NativePlayer.MediaStreamSeekCallback _mediaStreamVideoSeekCallback;
90
91         private bool _callbackRegistered;
92
93         private void RegisterEvents()
94         {
95             if (_callbackRegistered)
96             {
97                 return;
98             }
99             RegisterSubtitleUpdatedCallback();
100             RegisterErrorOccurredCallback();
101             RegisterPlaybackInterruptedCallback();
102             RegisterVideoStreamChangedCallback();
103             RegisterBufferingCallback();
104             RegisterMediaStreamBufferStatusCallback();
105             RegisterMediaStreamSeekCallback();
106             RegisterPlaybackCompletedCallback();
107
108             _callbackRegistered = true;
109         }
110
111         private void RegisterSubtitleUpdatedCallback()
112         {
113             _subtitleUpdatedCallback = (duration, text, _) =>
114             {
115                 Log.Debug(PlayerLog.Tag, $"duration : {duration}, text : {text}");
116                 SubtitleUpdated?.Invoke(this, new SubtitleUpdatedEventArgs(duration, text));
117             };
118
119             NativePlayer.SetSubtitleUpdatedCb(Handle, _subtitleUpdatedCallback).
120                 ThrowIfFailed(this, "Failed to initialize the player");
121         }
122
123         private void RegisterPlaybackCompletedCallback()
124         {
125             _playbackCompletedCallback = _ =>
126             {
127                 Log.Debug(PlayerLog.Tag, "completed callback");
128                 PlaybackCompleted?.Invoke(this, EventArgs.Empty);
129             };
130             NativePlayer.SetCompletedCb(Handle, _playbackCompletedCallback).
131                 ThrowIfFailed(this, "Failed to set PlaybackCompleted");
132         }
133
134         private void RegisterPlaybackInterruptedCallback()
135         {
136             _playbackInterruptedCallback = (code, _) =>
137             {
138                 if (!Enum.IsDefined(typeof(PlaybackInterruptionReason), code))
139                 {
140                     return;
141                 }
142
143                 if (code == PlaybackInterruptionReason.ResourceConflict)
144                 {
145                     OnUnprepared();
146                 }
147
148                 Log.Warn(PlayerLog.Tag, $"interrupted reason : {code}");
149                 PlaybackInterrupted?.Invoke(this, new PlaybackInterruptedEventArgs(code));
150             };
151
152             NativePlayer.SetInterruptedCb(Handle, _playbackInterruptedCallback).
153                 ThrowIfFailed(this, "Failed to set PlaybackInterrupted");
154         }
155
156         private void RegisterErrorOccurredCallback()
157         {
158             _playbackErrorCallback = (code, _) =>
159             {
160                 //TODO handle service disconnected error.
161                 Log.Warn(PlayerLog.Tag, "error code : " + code);
162                 ErrorOccurred?.Invoke(this, new PlayerErrorOccurredEventArgs((PlayerError)code));
163             };
164
165             NativePlayer.SetErrorCb(Handle, _playbackErrorCallback).
166                 ThrowIfFailed(this, "Failed to set PlaybackError");
167         }
168
169         #region VideoFrameDecoded event
170
171         private EventHandler<VideoFrameDecodedEventArgs> _videoFrameDecoded;
172
173         private NativePlayer.VideoFrameDecodedCallback _videoFrameDecodedCallback;
174
175         /// <summary>
176         /// Occurs when a video frame is decoded.
177         /// </summary>
178         /// <remarks>
179         ///     <para>The event handler will be executed on an internal thread.</para>
180         ///     <para>The <see cref="VideoFrameDecodedEventArgs.Packet"/> in event args should be disposed after use.</para>
181         /// </remarks>
182         /// <feature>http://tizen.org/feature/multimedia.raw_video</feature>
183         /// <exception cref="NotSupportedException">The required feature is not supported.</exception>
184         /// <seealso cref="VideoFrameDecodedEventArgs.Packet"/>
185         /// <since_tizen> 3 </since_tizen>
186         public event EventHandler<VideoFrameDecodedEventArgs> VideoFrameDecoded
187         {
188             add
189             {
190                 ValidationUtil.ValidateFeatureSupported(PlayerFeatures.RawVideo);
191
192                 _videoFrameDecoded += value;
193             }
194             remove
195             {
196                 ValidationUtil.ValidateFeatureSupported(PlayerFeatures.RawVideo);
197
198                 _videoFrameDecoded -= value;
199             }
200         }
201
202         private void RegisterVideoFrameDecodedCallback()
203         {
204             _videoFrameDecodedCallback = (packetHandle, _) =>
205             {
206                 var handler = _videoFrameDecoded;
207                 if (handler != null)
208                 {
209                     Log.Debug(PlayerLog.Tag, "packet : " + packetHandle);
210                     handler.Invoke(this,
211                         new VideoFrameDecodedEventArgs(MediaPacket.From(packetHandle)));
212                 }
213                 else
214                 {
215                     MediaPacket.From(packetHandle).Dispose();
216                 }
217             };
218
219             NativePlayer.SetVideoFrameDecodedCb(Handle, _videoFrameDecodedCallback).
220                 ThrowIfFailed(this, "Failed to register the VideoFrameDecoded");
221         }
222         #endregion
223
224         private void RegisterVideoStreamChangedCallback()
225         {
226             _videoStreamChangedCallback = (width, height, fps, bitrate, _) =>
227             {
228                 Log.Debug(PlayerLog.Tag, $"height={height}, width={width}, fps={fps}, bitrate={bitrate}");
229
230                 VideoStreamChanged?.Invoke(this, new VideoStreamChangedEventArgs(height, width, fps, bitrate));
231             };
232
233             NativePlayer.SetVideoStreamChangedCb(Handle, _videoStreamChangedCallback).
234                 ThrowIfFailed(this, "Failed to set the video stream changed callback");
235         }
236
237         private void RegisterBufferingCallback()
238         {
239             _bufferingProgressCallback = (percent, _) =>
240             {
241                 Log.Debug(PlayerLog.Tag, $"Buffering callback with percent { percent }");
242
243                 BufferingProgressChanged?.Invoke(this, new BufferingProgressChangedEventArgs(percent));
244             };
245
246             NativePlayer.SetBufferingCb(Handle, _bufferingProgressCallback).
247                 ThrowIfFailed(this, "Failed to set BufferingProgress");
248         }
249
250         private void RegisterMediaStreamBufferStatusCallback()
251         {
252             _mediaStreamAudioBufferStatusChangedCallback = (status, _) =>
253             {
254                 Debug.Assert(Enum.IsDefined(typeof(MediaStreamBufferStatus), status));
255                 Log.Debug(PlayerLog.Tag, "audio buffer status : " + status);
256
257                 MediaStreamAudioBufferStatusChanged?.Invoke(this,
258                     new MediaStreamBufferStatusChangedEventArgs(status));
259             };
260             _mediaStreamVideoBufferStatusChangedCallback = (status, _) =>
261             {
262                 Debug.Assert(Enum.IsDefined(typeof(MediaStreamBufferStatus), status));
263                 Log.Debug(PlayerLog.Tag, "video buffer status : " + status);
264
265                 MediaStreamVideoBufferStatusChanged?.Invoke(this,
266                     new MediaStreamBufferStatusChangedEventArgs(status));
267             };
268
269             RegisterMediaStreamBufferStatusCallback(StreamType.Audio, _mediaStreamAudioBufferStatusChangedCallback);
270             RegisterMediaStreamBufferStatusCallback(StreamType.Video, _mediaStreamVideoBufferStatusChangedCallback);
271         }
272
273         private void RegisterMediaStreamBufferStatusCallback(StreamType streamType,
274             NativePlayer.MediaStreamBufferStatusCallback cb)
275         {
276             NativePlayer.SetMediaStreamBufferStatusCb(Handle, streamType, cb).
277                 ThrowIfFailed(this, "Failed to SetMediaStreamBufferStatus");
278         }
279
280         private void RegisterMediaStreamSeekCallback()
281         {
282             _mediaStreamAudioSeekCallback = (offset, _) =>
283             {
284                 Log.Debug(PlayerLog.Tag, "audio seeking offset : " + offset);
285                 MediaStreamAudioSeekingOccurred?.Invoke(this, new MediaStreamSeekingOccurredEventArgs(offset));
286             };
287             _mediaStreamVideoSeekCallback = (offset, _) =>
288             {
289                 Log.Debug(PlayerLog.Tag, "video seeking offset : " + offset);
290                 MediaStreamVideoSeekingOccurred?.Invoke(this, new MediaStreamSeekingOccurredEventArgs(offset));
291             };
292
293             RegisterMediaStreamSeekCallback(StreamType.Audio, _mediaStreamAudioSeekCallback);
294             RegisterMediaStreamSeekCallback(StreamType.Video, _mediaStreamVideoSeekCallback);
295         }
296
297         private void RegisterMediaStreamSeekCallback(StreamType streamType, NativePlayer.MediaStreamSeekCallback cb)
298         {
299             NativePlayer.SetMediaStreamSeekCb(Handle, streamType, cb).
300                 ThrowIfFailed(this, "Failed to SetMediaStreamSeek");
301         }
302     }
303 }