06593344f6
The native code was receiving the event, but not reasing the event. Which [apparently](https://github.com/microsoft/react-native-windows/issues/4206) needs to be prefixed with "top" and then that turns into "onError". Note that this raises the event, but no error object is included. Better than nothing.
255 lines
9.0 KiB
C++
255 lines
9.0 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 &) {
|
|
runOnQueue([weak_this{get_weak()}]() {
|
|
if (auto strong_this{weak_this.get()}) {
|
|
strong_this->m_reactContext.DispatchEvent(*strong_this, L"topError", nullptr);
|
|
}
|
|
});
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
void ReactVideoView::Set_PlaybackRate(double rate) {
|
|
if (m_player != nullptr) {
|
|
m_player.PlaybackSession().PlaybackRate(rate);
|
|
}
|
|
}
|
|
|
|
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
|