react-native-video/windows/ReactNativeVideoCPP/ReactVideoView.cpp
Chris Glein 06593344f6 Fix lack of onError event
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.
2023-09-18 18:08:21 -07:00

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