/*
* 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.
///
///
/// If the reason is ,
/// the player state will be one of , ,
/// or .
///
///
/// 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");
}
}
}