WebVideoFullscreenModelVideoElement.mm [plain text]
/*
* Copyright (C) 2014 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "config.h"
#if PLATFORM(IOS)
#import "WebVideoFullscreenModelVideoElement.h"
#import "DOMEventInternal.h"
#import "Logging.h"
#import "MediaControlsHost.h"
#import "WebVideoFullscreenInterface.h"
#import <QuartzCore/CoreAnimation.h>
#import <WebCore/DOMEventListener.h>
#import <WebCore/Event.h>
#import <WebCore/EventListener.h>
#import <WebCore/EventNames.h>
#import <WebCore/HTMLElement.h>
#import <WebCore/HTMLVideoElement.h>
#import <WebCore/Page.h>
#import <WebCore/PageGroup.h>
#import <WebCore/SoftLinking.h>
#import <WebCore/TextTrackList.h>
#import <WebCore/TimeRanges.h>
#import <WebCore/WebCoreThreadRun.h>
#import <wtf/NeverDestroyed.h>
#import <wtf/RetainPtr.h>
using namespace WebCore;
WebVideoFullscreenModelVideoElement::WebVideoFullscreenModelVideoElement()
: EventListener(EventListener::CPPEventListenerType)
{
}
WebVideoFullscreenModelVideoElement::~WebVideoFullscreenModelVideoElement()
{
}
void WebVideoFullscreenModelVideoElement::setWebVideoFullscreenInterface(WebVideoFullscreenInterface* interface)
{
if (interface == m_videoFullscreenInterface)
return;
m_videoFullscreenInterface = interface;
if (m_videoFullscreenInterface) {
m_videoFullscreenInterface->resetMediaState();
if (m_videoElement) {
m_videoFullscreenInterface->setVideoDimensions(true, m_videoElement->videoWidth(), m_videoElement->videoHeight());
m_videoFullscreenInterface->setWirelessVideoPlaybackDisabled(m_videoElement->mediaSession().wirelessVideoPlaybackDisabled(*m_videoElement));
}
}
}
void WebVideoFullscreenModelVideoElement::setVideoElement(HTMLVideoElement* videoElement)
{
if (m_videoElement == videoElement)
return;
if (m_videoFullscreenInterface)
m_videoFullscreenInterface->resetMediaState();
if (m_videoElement && m_videoElement->videoFullscreenLayer())
m_videoElement->setVideoFullscreenLayer(nullptr);
if (m_videoElement && m_isListening) {
for (auto eventName : observedEventNames())
m_videoElement->removeEventListener(eventName, this, false);
}
m_isListening = false;
m_videoElement = videoElement;
if (!m_videoElement)
return;
for (auto eventName : observedEventNames())
m_videoElement->addEventListener(eventName, this, false);
m_isListening = true;
updateForEventName(eventNameAll());
if (m_videoFullscreenInterface) {
m_videoFullscreenInterface->setVideoDimensions(true, videoElement->videoWidth(), videoElement->videoHeight());
m_videoFullscreenInterface->setWirelessVideoPlaybackDisabled(m_videoElement->mediaSession().wirelessVideoPlaybackDisabled(*m_videoElement));
}
}
void WebVideoFullscreenModelVideoElement::handleEvent(WebCore::ScriptExecutionContext*, WebCore::Event* event)
{
LOG(Media, "handleEvent %s", event->type().characters8());
updateForEventName(event->type());
}
void WebVideoFullscreenModelVideoElement::updateForEventName(const WTF::AtomicString& eventName)
{
if (!m_videoElement || !m_videoFullscreenInterface)
return;
bool all = eventName == eventNameAll();
if (all
|| eventName == eventNames().durationchangeEvent) {
m_videoFullscreenInterface->setDuration(m_videoElement->duration());
// These is no standard event for minFastReverseRateChange; duration change is a reasonable proxy for it.
// It happens every time a new item becomes ready to play.
m_videoFullscreenInterface->setCanPlayFastReverse(m_videoElement->minFastReverseRate() < 0.0);
}
if (all
|| eventName == eventNames().pauseEvent
|| eventName == eventNames().playEvent
|| eventName == eventNames().ratechangeEvent)
m_videoFullscreenInterface->setRate(!m_videoElement->paused(), m_videoElement->playbackRate());
if (all
|| eventName == eventNames().timeupdateEvent) {
m_videoFullscreenInterface->setCurrentTime(m_videoElement->currentTime(), [[NSProcessInfo processInfo] systemUptime]);
m_videoFullscreenInterface->setBufferedTime(m_videoElement->maxBufferedTime());
// FIXME: 130788 - find a better event to update seekable ranges from.
m_videoFullscreenInterface->setSeekableRanges(*m_videoElement->seekable());
}
if (all
|| eventName == eventNames().addtrackEvent
|| eventName == eventNames().removetrackEvent)
updateLegibleOptions();
if (all
|| eventName == eventNames().webkitcurrentplaybacktargetiswirelesschangedEvent) {
bool enabled = m_videoElement->webkitCurrentPlaybackTargetIsWireless();
WebVideoFullscreenInterface::ExternalPlaybackTargetType targetType = WebVideoFullscreenInterface::TargetTypeNone;
String localizedDeviceName;
if (m_videoElement->mediaControlsHost()) {
DEPRECATED_DEFINE_STATIC_LOCAL(String, airplay, (ASCIILiteral("airplay")));
DEPRECATED_DEFINE_STATIC_LOCAL(String, tvout, (ASCIILiteral("tvout")));
String type = m_videoElement->mediaControlsHost()->externalDeviceType();
if (type == airplay)
targetType = WebVideoFullscreenInterface::TargetTypeAirPlay;
else if (type == tvout)
targetType = WebVideoFullscreenInterface::TargetTypeTVOut;
localizedDeviceName = m_videoElement->mediaControlsHost()->externalDeviceDisplayName();
}
m_videoFullscreenInterface->setExternalPlayback(enabled, targetType, localizedDeviceName);
m_videoFullscreenInterface->setWirelessVideoPlaybackDisabled(m_videoElement->mediaSession().wirelessVideoPlaybackDisabled(*m_videoElement));
}
}
void WebVideoFullscreenModelVideoElement::setVideoFullscreenLayer(PlatformLayer* videoLayer)
{
if (m_videoFullscreenLayer == videoLayer)
return;
m_videoFullscreenLayer = videoLayer;
[m_videoFullscreenLayer setAnchorPoint:CGPointMake(0.5, 0.5)];
[m_videoFullscreenLayer setBounds:m_videoFrame];
if (m_videoElement)
m_videoElement->setVideoFullscreenLayer(m_videoFullscreenLayer.get());
}
void WebVideoFullscreenModelVideoElement::play()
{
if (m_videoElement)
m_videoElement->play();
}
void WebVideoFullscreenModelVideoElement::pause()
{
if (m_videoElement)
m_videoElement->pause();
}
void WebVideoFullscreenModelVideoElement::togglePlayState()
{
if (m_videoElement)
m_videoElement->togglePlayState();
}
void WebVideoFullscreenModelVideoElement::beginScrubbing()
{
if (m_videoElement)
m_videoElement->beginScrubbing();
}
void WebVideoFullscreenModelVideoElement::endScrubbing()
{
if (m_videoElement)
m_videoElement->endScrubbing();
}
void WebVideoFullscreenModelVideoElement::seekToTime(double time)
{
if (m_videoElement)
m_videoElement->setCurrentTime(time);
}
void WebVideoFullscreenModelVideoElement::fastSeek(double time)
{
if (m_videoElement)
m_videoElement->fastSeek(time);
}
void WebVideoFullscreenModelVideoElement::beginScanningForward()
{
if (m_videoElement)
m_videoElement->beginScanning(MediaControllerInterface::Forward);
}
void WebVideoFullscreenModelVideoElement::beginScanningBackward()
{
if (m_videoElement)
m_videoElement->beginScanning(MediaControllerInterface::Backward);
}
void WebVideoFullscreenModelVideoElement::endScanning()
{
if (m_videoElement)
m_videoElement->endScanning();
}
void WebVideoFullscreenModelVideoElement::requestExitFullscreen()
{
if (m_videoElement && m_videoElement->isFullscreen())
m_videoElement->exitFullscreen();
}
void WebVideoFullscreenModelVideoElement::setVideoLayerFrame(FloatRect rect)
{
m_videoFrame = rect;
[m_videoFullscreenLayer setBounds:CGRect(rect)];
if (m_videoElement)
m_videoElement->setVideoFullscreenFrame(rect);
}
void WebVideoFullscreenModelVideoElement::setVideoLayerGravity(WebVideoFullscreenModel::VideoGravity gravity)
{
MediaPlayer::VideoGravity videoGravity = MediaPlayer::VideoGravityResizeAspect;
if (gravity == WebVideoFullscreenModel::VideoGravityResize)
videoGravity = MediaPlayer::VideoGravityResize;
else if (gravity == WebVideoFullscreenModel::VideoGravityResizeAspect)
videoGravity = MediaPlayer::VideoGravityResizeAspect;
else if (gravity == WebVideoFullscreenModel::VideoGravityResizeAspectFill)
videoGravity = MediaPlayer::VideoGravityResizeAspectFill;
else
ASSERT_NOT_REACHED();
m_videoElement->setVideoFullscreenGravity(videoGravity);
}
void WebVideoFullscreenModelVideoElement::selectAudioMediaOption(uint64_t selectedAudioIndex)
{
AudioTrack* selectedAudioTrack = nullptr;
for (size_t index = 0; index < m_audioTracksForMenu.size(); ++index) {
auto& audioTrack = m_audioTracksForMenu[index];
audioTrack->setEnabled(index == static_cast<size_t>(selectedAudioIndex));
if (audioTrack->enabled())
selectedAudioTrack = audioTrack.get();
}
m_videoElement->audioTrackEnabledChanged(selectedAudioTrack);
}
void WebVideoFullscreenModelVideoElement::selectLegibleMediaOption(uint64_t index)
{
TextTrack* textTrack = nullptr;
if (index < m_legibleTracksForMenu.size())
textTrack = m_legibleTracksForMenu[static_cast<size_t>(index)].get();
else
textTrack = TextTrack::captionMenuOffItem();
m_videoElement->setSelectedTextTrack(textTrack);
}
void WebVideoFullscreenModelVideoElement::updateLegibleOptions()
{
AudioTrackList* audioTrackList = m_videoElement->audioTracks();
TextTrackList* trackList = m_videoElement->textTracks();
if ((!trackList && !audioTrackList) || !m_videoElement->document().page() || !m_videoElement->mediaControlsHost())
return;
WTF::AtomicString displayMode = m_videoElement->mediaControlsHost()->captionDisplayMode();
TextTrack* offItem = m_videoElement->mediaControlsHost()->captionMenuOffItem();
TextTrack* automaticItem = m_videoElement->mediaControlsHost()->captionMenuAutomaticItem();
CaptionUserPreferences& captionPreferences = *m_videoElement->document().page()->group().captionPreferences();
m_legibleTracksForMenu = captionPreferences.sortedTrackListForMenu(trackList);
m_audioTracksForMenu = captionPreferences.sortedTrackListForMenu(audioTrackList);
Vector<String> audioTrackDisplayNames;
uint64_t selectedAudioIndex = 0;
for (size_t index = 0; index < m_audioTracksForMenu.size(); ++index) {
auto& track = m_audioTracksForMenu[index];
audioTrackDisplayNames.append(captionPreferences.displayNameForTrack(track.get()));
if (track->enabled())
selectedAudioIndex = index;
}
m_videoFullscreenInterface->setAudioMediaSelectionOptions(audioTrackDisplayNames, selectedAudioIndex);
Vector<String> trackDisplayNames;
uint64_t selectedIndex = 0;
uint64_t offIndex = 0;
bool trackMenuItemSelected = false;
for (size_t index = 0; index < m_legibleTracksForMenu.size(); index++) {
auto& track = m_legibleTracksForMenu[index];
trackDisplayNames.append(captionPreferences.displayNameForTrack(track.get()));
if (track == offItem)
offIndex = index;
if (track == automaticItem && displayMode == MediaControlsHost::automaticKeyword()) {
selectedIndex = index;
trackMenuItemSelected = true;
}
if (displayMode != MediaControlsHost::automaticKeyword() && track->mode() == TextTrack::showingKeyword()) {
selectedIndex = index;
trackMenuItemSelected = true;
}
}
if (offIndex && !trackMenuItemSelected && displayMode == MediaControlsHost::forcedOnlyKeyword()) {
selectedIndex = offIndex;
trackMenuItemSelected = true;
}
m_videoFullscreenInterface->setLegibleMediaSelectionOptions(trackDisplayNames, selectedIndex);
}
const Vector<AtomicString>& WebVideoFullscreenModelVideoElement::observedEventNames()
{
static NeverDestroyed<Vector<AtomicString>> sEventNames;
if (!sEventNames.get().size()) {
sEventNames.get().append(eventNames().durationchangeEvent);
sEventNames.get().append(eventNames().pauseEvent);
sEventNames.get().append(eventNames().playEvent);
sEventNames.get().append(eventNames().ratechangeEvent);
sEventNames.get().append(eventNames().timeupdateEvent);
sEventNames.get().append(eventNames().addtrackEvent);
sEventNames.get().append(eventNames().removetrackEvent);
sEventNames.get().append(eventNames().webkitcurrentplaybacktargetiswirelesschangedEvent);
}
return sEventNames.get();
}
const AtomicString& WebVideoFullscreenModelVideoElement::eventNameAll()
{
static NeverDestroyed<AtomicString> sEventNameAll = "allEvents";
return sEventNameAll;
}
void WebVideoFullscreenModelVideoElement::fullscreenModeChanged(HTMLMediaElementEnums::VideoFullscreenMode videoFullscreenMode)
{
if (m_videoElement)
m_videoElement->fullscreenModeChanged(videoFullscreenMode);
}
#endif