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