/* * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the License); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an AS IS BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Threading.Tasks; using System.Runtime.InteropServices; using System.Diagnostics; using System.IO; using System.Threading; using static Interop; namespace Tizen.Multimedia { public partial class Player { /// /// Occurs when the playback of a media is finished. /// /// 3 public event EventHandler PlaybackCompleted; private NativePlayer.PlaybackCompletedCallback _playbackCompletedCallback; /// /// Occurs when the playback of a media is interrupted. /// /// 3 public event EventHandler PlaybackInterrupted; private NativePlayer.PlaybackInterruptedCallback _playbackInterruptedCallback; /// /// Occurs when any error occurs. /// /// The event handler will be executed on an internal thread. /// 3 public event EventHandler ErrorOccurred; private NativePlayer.PlaybackErrorCallback _playbackErrorCallback; /// /// Occurs when the video stream is changed. /// /// The event handler will be executed on an internal thread. /// 3 public event EventHandler VideoStreamChanged; private NativePlayer.VideoStreamChangedCallback _videoStreamChangedCallback; /// /// Occurs when the subtitle is updated. /// /// The event handler will be executed on an internal thread. /// 3 public event EventHandler SubtitleUpdated; private NativePlayer.SubtitleUpdatedCallback _subtitleUpdatedCallback; /// /// Occurs when there is a change in the buffering status of streaming. /// /// 3 public event EventHandler BufferingProgressChanged; private NativePlayer.BufferingProgressCallback _bufferingProgressCallback; internal event EventHandler MediaStreamAudioBufferStatusChanged; private NativePlayer.MediaStreamBufferStatusCallback _mediaStreamAudioBufferStatusChangedCallback; internal event EventHandler MediaStreamVideoBufferStatusChanged; private NativePlayer.MediaStreamBufferStatusCallback _mediaStreamVideoBufferStatusChangedCallback; internal event EventHandler MediaStreamAudioSeekingOccurred; private NativePlayer.MediaStreamSeekCallback _mediaStreamAudioSeekCallback; internal event EventHandler MediaStreamVideoSeekingOccurred; private NativePlayer.MediaStreamSeekCallback _mediaStreamVideoSeekCallback; private bool _callbackRegistered; private void RegisterEvents() { if (_callbackRegistered) { return; } RegisterSubtitleUpdatedCallback(); RegisterErrorOccurredCallback(); RegisterPlaybackInterruptedCallback(); RegisterVideoStreamChangedCallback(); RegisterBufferingCallback(); RegisterMediaStreamBufferStatusCallback(); RegisterMediaStreamSeekCallback(); RegisterPlaybackCompletedCallback(); _callbackRegistered = true; } private void RegisterSubtitleUpdatedCallback() { _subtitleUpdatedCallback = (duration, text, _) => { Log.Debug(PlayerLog.Tag, "duration : " + duration + ", text : " + text); SubtitleUpdated?.Invoke(this, new SubtitleUpdatedEventArgs(duration, text)); }; NativePlayer.SetSubtitleUpdatedCb(Handle, _subtitleUpdatedCallback). ThrowIfFailed("Failed to initialize the player"); } private void RegisterPlaybackCompletedCallback() { _playbackCompletedCallback = _ => { Log.Debug(PlayerLog.Tag, "completed callback"); PlaybackCompleted?.Invoke(this, EventArgs.Empty); }; NativePlayer.SetCompletedCb(Handle, _playbackCompletedCallback). ThrowIfFailed("Failed to set PlaybackCompleted"); } private void RegisterPlaybackInterruptedCallback() { _playbackInterruptedCallback = (code, _) => { if (!Enum.IsDefined(typeof(PlaybackInterruptionReason), code)) { return; } if (code == PlaybackInterruptionReason.ResourceConflict) { OnUnprepared(); } Log.Warn(PlayerLog.Tag, "interrupted reason : " + code); PlaybackInterrupted?.Invoke(this, new PlaybackInterruptedEventArgs(code)); }; NativePlayer.SetInterruptedCb(Handle, _playbackInterruptedCallback). ThrowIfFailed("Failed to set PlaybackInterrupted"); } private void RegisterErrorOccurredCallback() { _playbackErrorCallback = (code, _) => { //TODO handle service disconnected error. Log.Warn(PlayerLog.Tag, "error code : " + code); ErrorOccurred?.Invoke(this, new PlayerErrorOccurredEventArgs((PlayerError)code)); }; NativePlayer.SetErrorCb(Handle, _playbackErrorCallback). ThrowIfFailed("Failed to set PlaybackError"); } #region VideoFrameDecoded event private EventHandler _videoFrameDecoded; private NativePlayer.VideoFrameDecodedCallback _videoFrameDecodedCallback; /// /// Occurs when a video frame is decoded. /// /// /// The event handler will be executed on an internal thread. /// The in event args should be disposed after use. /// /// http://tizen.org/feature/multimedia.raw_video /// The required feature is not supported. /// /// 3 public event EventHandler VideoFrameDecoded { add { ValidationUtil.ValidateFeatureSupported(PlayerFeatures.RawVideo); _videoFrameDecoded += value; } remove { ValidationUtil.ValidateFeatureSupported(PlayerFeatures.RawVideo); _videoFrameDecoded -= value; } } private void RegisterVideoFrameDecodedCallback() { _videoFrameDecodedCallback = (packetHandle, _) => { var handler = _videoFrameDecoded; if (handler != null) { Log.Debug(PlayerLog.Tag, "packet : " + packetHandle); handler.Invoke(this, new VideoFrameDecodedEventArgs(MediaPacket.From(packetHandle))); } else { MediaPacket.From(packetHandle).Dispose(); } }; NativePlayer.SetVideoFrameDecodedCb(Handle, _videoFrameDecodedCallback). ThrowIfFailed("Failed to register the VideoFrameDecoded"); } #endregion private void RegisterVideoStreamChangedCallback() { ValidatePlayerState(PlayerState.Idle); _videoStreamChangedCallback = (width, height, fps, bitrate, _) => { Log.Debug(PlayerLog.Tag, "height : " + height + ", width : " + width + ", fps : " + fps + ", bitrate : " + bitrate); VideoStreamChanged?.Invoke(this, new VideoStreamChangedEventArgs(height, width, fps, bitrate)); }; NativePlayer.SetVideoStreamChangedCb(Handle, _videoStreamChangedCallback). ThrowIfFailed("Failed to set the video stream changed callback"); } private void RegisterBufferingCallback() { _bufferingProgressCallback = (percent, _) => { Log.Debug(PlayerLog.Tag, $"Buffering callback with percent { percent }"); BufferingProgressChanged?.Invoke(this, new BufferingProgressChangedEventArgs(percent)); }; NativePlayer.SetBufferingCb(Handle, _bufferingProgressCallback). ThrowIfFailed("Failed to set BufferingProgress"); } private void RegisterMediaStreamBufferStatusCallback() { _mediaStreamAudioBufferStatusChangedCallback = (status, _) => { Debug.Assert(Enum.IsDefined(typeof(MediaStreamBufferStatus), status)); Log.Debug(PlayerLog.Tag, "audio buffer status : " + status); MediaStreamAudioBufferStatusChanged?.Invoke(this, new MediaStreamBufferStatusChangedEventArgs(status)); }; _mediaStreamVideoBufferStatusChangedCallback = (status, _) => { Debug.Assert(Enum.IsDefined(typeof(MediaStreamBufferStatus), status)); Log.Debug(PlayerLog.Tag, "video buffer status : " + status); MediaStreamVideoBufferStatusChanged?.Invoke(this, new MediaStreamBufferStatusChangedEventArgs(status)); }; RegisterMediaStreamBufferStatusCallback(StreamType.Audio, _mediaStreamAudioBufferStatusChangedCallback); RegisterMediaStreamBufferStatusCallback(StreamType.Video, _mediaStreamVideoBufferStatusChangedCallback); } private void RegisterMediaStreamBufferStatusCallback(StreamType streamType, NativePlayer.MediaStreamBufferStatusCallback cb) { NativePlayer.SetMediaStreamBufferStatusCb(Handle, streamType, cb). ThrowIfFailed("Failed to SetMediaStreamBufferStatus"); } private void RegisterMediaStreamSeekCallback() { _mediaStreamAudioSeekCallback = (offset, _) => { Log.Debug(PlayerLog.Tag, "audio seeking offset : " + offset); MediaStreamAudioSeekingOccurred?.Invoke(this, new MediaStreamSeekingOccurredEventArgs(offset)); }; _mediaStreamVideoSeekCallback = (offset, _) => { Log.Debug(PlayerLog.Tag, "video seeking offset : " + offset); MediaStreamVideoSeekingOccurred?.Invoke(this, new MediaStreamSeekingOccurredEventArgs(offset)); }; RegisterMediaStreamSeekCallback(StreamType.Audio, _mediaStreamAudioSeekCallback); RegisterMediaStreamSeekCallback(StreamType.Video, _mediaStreamVideoSeekCallback); } private void RegisterMediaStreamSeekCallback(StreamType streamType, NativePlayer.MediaStreamSeekCallback cb) { NativePlayer.SetMediaStreamSeekCb(Handle, streamType, cb). ThrowIfFailed("Failed to SetMediaStreamSeek"); } } }