245 lines
9.3 KiB
C++
245 lines
9.3 KiB
C++
|
#include "pch.h"
|
||
|
#include "ReactVideoView.h"
|
||
|
#include "ReactVideoView.g.cpp"
|
||
|
#include "NativeModules.h"
|
||
|
|
||
|
using namespace winrt;
|
||
|
using namespace Windows::Foundation;
|
||
|
using namespace Windows::Foundation::Collections;
|
||
|
|
||
|
using namespace Windows::UI::Xaml;
|
||
|
using namespace Windows::UI::Xaml::Media;
|
||
|
using namespace Windows::UI::Xaml::Controls;
|
||
|
using namespace Windows::UI::Core;
|
||
|
using namespace Windows::Media::Core;
|
||
|
using namespace Windows::Media::Playback;
|
||
|
|
||
|
namespace winrt::ReactNativeVideoCPP::implementation {
|
||
|
|
||
|
ReactVideoView::ReactVideoView(winrt::Microsoft::ReactNative::IReactContext const& reactContext)
|
||
|
: m_reactContext(reactContext){
|
||
|
// always create and set the player here instead of depending on auto-create logic
|
||
|
// in the MediaPlayerElement (only when auto play is on or URI is set)
|
||
|
m_player = winrt::Windows::Media::Playback::MediaPlayer();
|
||
|
SetMediaPlayer(m_player);
|
||
|
m_uiDispatcher = CoreWindow::GetForCurrentThread().Dispatcher();
|
||
|
|
||
|
m_mediaOpenedToken = m_player.MediaOpened(winrt::auto_revoke, [ref = get_weak()](auto const& sender, auto const& args) {
|
||
|
if (auto self = ref.get()) {
|
||
|
self->OnMediaOpened(sender, args);
|
||
|
}
|
||
|
});
|
||
|
m_mediaFailedToken = m_player.MediaFailed(winrt::auto_revoke, [ref = get_weak()](auto const& sender, auto const& args) {
|
||
|
if (auto self = ref.get()) {
|
||
|
self->OnMediaFailed(sender, args);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
m_mediaEndedToken = m_player.MediaEnded(winrt::auto_revoke, [ref = get_weak()](auto const& sender, auto const& args) {
|
||
|
if (auto self = ref.get()) {
|
||
|
self->OnMediaEnded(sender, args);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
m_bufferingStartedToken =
|
||
|
m_player.PlaybackSession().BufferingStarted(winrt::auto_revoke, [ref = get_weak()](auto const& sender, auto const& args) {
|
||
|
if (auto self = ref.get()) {
|
||
|
self->OnBufferingStarted(sender, args);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
m_bufferingEndedToken =
|
||
|
m_player.PlaybackSession().BufferingEnded(winrt::auto_revoke, [ref = get_weak()](auto const& sender, auto const& args) {
|
||
|
if (auto self = ref.get()) {
|
||
|
self->OnBufferingEnded(sender, args);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
m_seekCompletedToken =
|
||
|
m_player.PlaybackSession().SeekCompleted(winrt::auto_revoke, [ref = get_weak()](auto const& sender, auto const& args) {
|
||
|
if (auto self = ref.get()) {
|
||
|
self->OnSeekCompleted(sender, args);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
m_timer = Windows::UI::Xaml::DispatcherTimer();
|
||
|
m_timer.Interval(std::chrono::milliseconds{ 250 });
|
||
|
m_timer.Start();
|
||
|
auto token = m_timer.Tick([ref = get_weak()](auto const&, auto const&) {
|
||
|
if (auto self = ref.get()){
|
||
|
if (auto mediaPlayer = self->m_player) {
|
||
|
if (mediaPlayer.PlaybackSession().PlaybackState() ==
|
||
|
winrt::Windows::Media::Playback::MediaPlaybackState::Playing) {
|
||
|
auto currentTimeInSeconds = mediaPlayer.PlaybackSession().Position().count() / 10000000;
|
||
|
self->m_reactContext.DispatchEvent(
|
||
|
*self,
|
||
|
L"topProgress",
|
||
|
[&](winrt::Microsoft::ReactNative::IJSValueWriter const& eventDataWriter) noexcept {
|
||
|
eventDataWriter.WriteObjectBegin();
|
||
|
{
|
||
|
WriteProperty(eventDataWriter, L"currentTime", currentTimeInSeconds);
|
||
|
WriteProperty(eventDataWriter, L"playableDuration", 0.0);
|
||
|
}
|
||
|
eventDataWriter.WriteObjectEnd();
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
void ReactVideoView::OnMediaOpened(IInspectable const&, IInspectable const&) {
|
||
|
runOnQueue([weak_this{ get_weak() }]() {
|
||
|
if (auto strong_this{ weak_this.get() }) {
|
||
|
if (auto mediaPlayer = strong_this->m_player) {
|
||
|
auto width = mediaPlayer.PlaybackSession().NaturalVideoWidth();
|
||
|
auto height = mediaPlayer.PlaybackSession().NaturalVideoHeight();
|
||
|
auto orientation = (width > height) ? L"landscape" : L"portrait";
|
||
|
auto durationInSeconds = mediaPlayer.PlaybackSession().NaturalDuration().count() / 10000000;
|
||
|
auto currentTimeInSeconds = mediaPlayer.PlaybackSession().Position().count() / 10000000;
|
||
|
|
||
|
strong_this->m_reactContext.DispatchEvent(
|
||
|
*strong_this,
|
||
|
L"topLoad",
|
||
|
[&](winrt::Microsoft::ReactNative::IJSValueWriter const& eventDataWriter) noexcept {
|
||
|
eventDataWriter.WriteObjectBegin();
|
||
|
{
|
||
|
WriteProperty(eventDataWriter, L"duration", durationInSeconds);
|
||
|
WriteProperty(eventDataWriter, L"currentTime", currentTimeInSeconds);
|
||
|
|
||
|
eventDataWriter.WritePropertyName(L"naturalSize");
|
||
|
{
|
||
|
eventDataWriter.WriteObjectBegin();
|
||
|
WriteProperty(eventDataWriter, L"width", width);
|
||
|
WriteProperty(eventDataWriter, L"height", height);
|
||
|
WriteProperty(eventDataWriter, L"orientation", orientation);
|
||
|
WriteProperty(eventDataWriter, L"orientation", orientation);
|
||
|
eventDataWriter.WriteObjectEnd();
|
||
|
}
|
||
|
|
||
|
WriteProperty(eventDataWriter, L"canPlayFastForward", false);
|
||
|
WriteProperty(eventDataWriter, L"canPlaySlowForward", false);
|
||
|
WriteProperty(eventDataWriter, L"canPlaySlow", false);
|
||
|
WriteProperty(eventDataWriter, L"canStepBackward", false);
|
||
|
WriteProperty(eventDataWriter, L"canStepForward", false);
|
||
|
}
|
||
|
eventDataWriter.WriteObjectEnd();
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
void ReactVideoView::OnMediaFailed(IInspectable const&, IInspectable const&) {}
|
||
|
|
||
|
void ReactVideoView::OnMediaEnded(IInspectable const&, IInspectable const&) {
|
||
|
runOnQueue([weak_this{ get_weak() }]() {
|
||
|
if (auto strong_this{ weak_this.get() }) {
|
||
|
strong_this->m_reactContext.DispatchEvent(*strong_this, L"topEnd", nullptr);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
void ReactVideoView::OnBufferingStarted(IInspectable const&, IInspectable const&) {}
|
||
|
|
||
|
void ReactVideoView::OnBufferingEnded(IInspectable const&, IInspectable const&) {}
|
||
|
|
||
|
void ReactVideoView::OnSeekCompleted(IInspectable const&, IInspectable const&) {
|
||
|
runOnQueue([weak_this{ get_weak() }]() {
|
||
|
if (auto strong_this{ weak_this.get() }) {
|
||
|
strong_this->m_reactContext.DispatchEvent(*strong_this, L"topSeek", nullptr);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
void ReactVideoView::Set_ProgressUpdateInterval(int64_t interval) {
|
||
|
m_timer.Interval(std::chrono::milliseconds{ interval });
|
||
|
}
|
||
|
|
||
|
void ReactVideoView::Set_IsLoopingEnabled(bool value) {
|
||
|
m_isLoopingEnabled = value;
|
||
|
if (m_player != nullptr) {
|
||
|
m_player.IsLoopingEnabled(m_isLoopingEnabled);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ReactVideoView::Set_UriString(hstring const& value) {
|
||
|
m_uriString = value;
|
||
|
if (m_player != nullptr) {
|
||
|
auto uri = Uri(m_uriString);
|
||
|
m_player.Source(MediaSource::CreateFromUri(uri));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ReactVideoView::Set_Paused(bool value) {
|
||
|
m_isPaused = value;
|
||
|
if (m_player != nullptr) {
|
||
|
if (m_isPaused) {
|
||
|
if (IsPlaying(m_player.PlaybackSession().PlaybackState())) {
|
||
|
m_player.Pause();
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
if (!IsPlaying(m_player.PlaybackSession().PlaybackState())) {
|
||
|
m_player.Play();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ReactVideoView::Set_AutoPlay(bool autoPlay) {
|
||
|
m_player.AutoPlay(autoPlay);
|
||
|
}
|
||
|
|
||
|
void ReactVideoView::Set_Muted(bool isMuted) {
|
||
|
m_isMuted = isMuted;
|
||
|
if (m_player != nullptr) {
|
||
|
m_player.IsMuted(m_isMuted);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ReactVideoView::Set_Controls(bool useControls) {
|
||
|
m_useControls = useControls;
|
||
|
AreTransportControlsEnabled(m_useControls);
|
||
|
}
|
||
|
|
||
|
void ReactVideoView::Set_FullScreen(bool fullScreen) {
|
||
|
m_fullScreen = fullScreen;
|
||
|
IsFullWindow(m_fullScreen);
|
||
|
|
||
|
if (m_fullScreen)
|
||
|
{
|
||
|
Set_Controls(true); // full window will always have transport control enabled
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ReactVideoView::Set_Volume(double volume) {
|
||
|
m_volume = volume;
|
||
|
if (m_player != nullptr) {
|
||
|
m_player.Volume(m_volume);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ReactVideoView::Set_Position(double position) {
|
||
|
m_position = position;
|
||
|
if (m_player != nullptr) {
|
||
|
std::chrono::seconds duration(static_cast<int>(m_position));
|
||
|
m_player.PlaybackSession().Position(duration);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool ReactVideoView::IsPlaying(MediaPlaybackState currentState) {
|
||
|
return (
|
||
|
currentState == MediaPlaybackState::Buffering ||
|
||
|
currentState == MediaPlaybackState::Opening ||
|
||
|
currentState == MediaPlaybackState::Playing
|
||
|
);
|
||
|
}
|
||
|
|
||
|
void ReactVideoView::runOnQueue(std::function<void()>&& func) {
|
||
|
m_uiDispatcher.RunAsync(
|
||
|
winrt::Windows::UI::Core::CoreDispatcherPriority::Normal, [func = std::move(func)]() { func(); });
|
||
|
}
|
||
|
|
||
|
} // namespace winrt::ReactNativeVideoCPP::implementation
|