WebVideoFullscreenModelMediaElement.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 "WebVideoFullscreenModelMediaElement.h"
#import "DOMEventInternal.h"
#import "Logging.h"
#import "MediaControlsHost.h"
#import "WebVideoFullscreenInterface.h"
#import <WebCore/DOMEventListener.h>
#import <WebCore/Event.h>
#import <WebCore/EventListener.h>
#import <WebCore/EventNames.h>
#import <WebCore/HTMLElement.h>
#import <WebCore/HTMLMediaElement.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 <QuartzCore/CoreAnimation.h>
#import <wtf/NeverDestroyed.h>
#import <wtf/RetainPtr.h>
using namespace WebCore;
WebVideoFullscreenModelMediaElement::WebVideoFullscreenModelMediaElement()
: EventListener(EventListener::CPPEventListenerType)
, m_isListening{false}
{
}
WebVideoFullscreenModelMediaElement::~WebVideoFullscreenModelMediaElement()
{
}
void WebVideoFullscreenModelMediaElement::setMediaElement(HTMLMediaElement* mediaElement)
{
if (m_mediaElement == mediaElement)
return;
if (m_mediaElement && m_isListening) {
for (auto eventName : observedEventNames())
m_mediaElement->removeEventListener(eventName, this, false);
}
m_isListening = false;
m_mediaElement = mediaElement;
if (!m_mediaElement)
return;
for (auto eventName : observedEventNames())
m_mediaElement->addEventListener(eventName, this, false);
m_isListening = true;
updateForEventName(eventNameAll());
if (isHTMLVideoElement(m_mediaElement.get())) {
HTMLVideoElement *videoElement = toHTMLVideoElement(m_mediaElement.get());
m_videoFullscreenInterface->setVideoDimensions(true, videoElement->videoWidth(), videoElement->videoHeight());
} else
m_videoFullscreenInterface->setVideoDimensions(false, 0, 0);
}
void WebVideoFullscreenModelMediaElement::handleEvent(WebCore::ScriptExecutionContext*, WebCore::Event* event)
{
LOG(Media, "handleEvent %s", event->type().characters8());
updateForEventName(event->type());
}
void WebVideoFullscreenModelMediaElement::updateForEventName(const WTF::AtomicString& eventName)
{
if (!m_mediaElement || !m_videoFullscreenInterface)
return;
bool all = eventName == eventNameAll();
if (all
|| eventName == eventNames().durationchangeEvent) {
m_videoFullscreenInterface->setDuration(m_mediaElement->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_mediaElement->minFastReverseRate() < 0.0);
}
if (all
|| eventName == eventNames().pauseEvent
|| eventName == eventNames().playEvent
|| eventName == eventNames().ratechangeEvent)
m_videoFullscreenInterface->setRate(!m_mediaElement->paused(), m_mediaElement->playbackRate());
if (all
|| eventName == eventNames().timeupdateEvent) {
m_videoFullscreenInterface->setCurrentTime(m_mediaElement->currentTime(), [[NSProcessInfo processInfo] systemUptime]);
// FIXME: 130788 - find a better event to update seekable ranges from.
m_videoFullscreenInterface->setSeekableRanges(*m_mediaElement->seekable());
}
if (all
|| eventName == eventNames().addtrackEvent
|| eventName == eventNames().removetrackEvent)
updateLegibleOptions();
if (all
|| eventName == eventNames().webkitcurrentplaybacktargetiswirelesschangedEvent) {
bool enabled = m_mediaElement->mediaSession().currentPlaybackTargetIsWireless(*m_mediaElement);
WebVideoFullscreenInterface::ExternalPlaybackTargetType targetType = WebVideoFullscreenInterface::TargetTypeNone;
String localizedDeviceName;
if (m_mediaElement->mediaControlsHost()) {
DEPRECATED_DEFINE_STATIC_LOCAL(String, airplay, (ASCIILiteral("airplay")));
DEPRECATED_DEFINE_STATIC_LOCAL(String, tvout, (ASCIILiteral("tvout")));
String type = m_mediaElement->mediaControlsHost()->externalDeviceType();
if (type == airplay)
targetType = WebVideoFullscreenInterface::TargetTypeAirPlay;
else if (type == tvout)
targetType = WebVideoFullscreenInterface::TargetTypeTVOut;
localizedDeviceName = m_mediaElement->mediaControlsHost()->externalDeviceDisplayName();
}
m_videoFullscreenInterface->setExternalPlayback(enabled, targetType, localizedDeviceName);
}
}
void WebVideoFullscreenModelMediaElement::setVideoFullscreenLayer(PlatformLayer* videoLayer)
{
if (m_videoFullscreenLayer == videoLayer)
return;
m_videoFullscreenLayer = videoLayer;
[m_videoFullscreenLayer setFrame:m_videoFrame];
__block RefPtr<WebVideoFullscreenModelMediaElement> protect(this);
WebThreadRun(^{
if (m_mediaElement)
m_mediaElement->setVideoFullscreenLayer(m_videoFullscreenLayer.get());
protect.clear();
});
}
void WebVideoFullscreenModelMediaElement::play() {
__block RefPtr<WebVideoFullscreenModelMediaElement> protect(this);
WebThreadRun(^{
if (m_mediaElement)
m_mediaElement->play();
protect.clear();
});
}
void WebVideoFullscreenModelMediaElement::pause()
{
__block RefPtr<WebVideoFullscreenModelMediaElement> protect(this);
WebThreadRun(^{
if (m_mediaElement)
m_mediaElement->pause();
protect.clear();
});
}
void WebVideoFullscreenModelMediaElement::togglePlayState()
{
__block RefPtr<WebVideoFullscreenModelMediaElement> protect(this);
WebThreadRun(^{
if (m_mediaElement)
m_mediaElement->togglePlayState();
protect.clear();
});
}
void WebVideoFullscreenModelMediaElement::beginScrubbing()
{
__block RefPtr<WebVideoFullscreenModelMediaElement> protect(this);
WebThreadRun(^{
if (m_mediaElement)
m_mediaElement->beginScrubbing();
protect.clear();
});
}
void WebVideoFullscreenModelMediaElement::endScrubbing()
{
__block RefPtr<WebVideoFullscreenModelMediaElement> protect(this);
WebThreadRun(^{
if (m_mediaElement)
m_mediaElement->endScrubbing();
protect.clear();
});
}
void WebVideoFullscreenModelMediaElement::seekToTime(double time)
{
__block RefPtr<WebVideoFullscreenModelMediaElement> protect(this);
WebThreadRun(^{
if (m_mediaElement)
m_mediaElement->setCurrentTime(time);
protect.clear();
});
}
void WebVideoFullscreenModelMediaElement::fastSeek(double time)
{
__block RefPtr<WebVideoFullscreenModelMediaElement> protect(this);
WebThreadRun(^{
if (m_mediaElement)
m_mediaElement->fastSeek(time);
protect.clear();
});
}
void WebVideoFullscreenModelMediaElement::beginScanningForward()
{
__block RefPtr<WebVideoFullscreenModelMediaElement> protect(this);
WebThreadRun(^{
if (m_mediaElement)
m_mediaElement->beginScanning(MediaControllerInterface::Forward);
protect.clear();
});
}
void WebVideoFullscreenModelMediaElement::beginScanningBackward()
{
__block RefPtr<WebVideoFullscreenModelMediaElement> protect(this);
WebThreadRun(^{
if (m_mediaElement)
m_mediaElement->beginScanning(MediaControllerInterface::Backward);
protect.clear();
});
}
void WebVideoFullscreenModelMediaElement::endScanning()
{
__block RefPtr<WebVideoFullscreenModelMediaElement> protect(this);
WebThreadRun(^{
if (m_mediaElement)
m_mediaElement->endScanning();
protect.clear();
});
}
void WebVideoFullscreenModelMediaElement::requestExitFullscreen()
{
if (!m_mediaElement)
return;
__block RefPtr<WebVideoFullscreenModelMediaElement> protect(this);
WebThreadRun(^{
if (m_mediaElement && m_mediaElement->isFullscreen())
m_mediaElement->exitFullscreen();
protect.clear();
});
}
void WebVideoFullscreenModelMediaElement::setVideoLayerFrame(FloatRect rect)
{
m_videoFrame = rect;
[m_videoFullscreenLayer setFrame:CGRect(rect)];
m_mediaElement->setVideoFullscreenFrame(rect);
}
void WebVideoFullscreenModelMediaElement::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_mediaElement->setVideoFullscreenGravity(videoGravity);
}
void WebVideoFullscreenModelMediaElement::selectAudioMediaOption(uint64_t)
{
// FIXME: 131236 Implement audio track selection.
}
void WebVideoFullscreenModelMediaElement::selectLegibleMediaOption(uint64_t index)
{
TextTrack* textTrack = nullptr;
if (index < m_legibleTracksForMenu.size())
textTrack = m_legibleTracksForMenu[static_cast<size_t>(index)].get();
m_mediaElement->setSelectedTextTrack(textTrack);
}
void WebVideoFullscreenModelMediaElement::updateLegibleOptions()
{
TextTrackList* trackList = m_mediaElement->textTracks();
if (!trackList || !m_mediaElement->document().page() || !m_mediaElement->mediaControlsHost())
return;
WTF::AtomicString displayMode = m_mediaElement->mediaControlsHost()->captionDisplayMode();
TextTrack* offItem = m_mediaElement->mediaControlsHost()->captionMenuOffItem();
TextTrack* automaticItem = m_mediaElement->mediaControlsHost()->captionMenuAutomaticItem();
CaptionUserPreferences& captionPreferences = *m_mediaElement->document().page()->group().captionPreferences();
m_legibleTracksForMenu = captionPreferences.sortedTrackListForMenu(trackList);
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>& WebVideoFullscreenModelMediaElement::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& WebVideoFullscreenModelMediaElement::eventNameAll()
{
static NeverDestroyed<AtomicString> sEventNameAll = "allEvents";
return sEventNameAll;
}
#endif