Deleted: trunk/Source/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm (227371 => 227372)
--- trunk/Source/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm 2018-01-23 01:09:45 UTC (rev 227371)
+++ trunk/Source/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm 2018-01-23 01:25:36 UTC (rev 227372)
@@ -1,1727 +0,0 @@
-/*
- * Copyright (C) 2007-2017 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 ENABLE(VIDEO) && USE(QTKIT)
-
-#import "MediaPlayerPrivateQTKit.h"
-
-#import "DocumentLoader.h"
-#import "GraphicsContext.h"
-#import "Logging.h"
-#import "MIMETypeRegistry.h"
-#import "MediaTimeQTKit.h"
-#import "PlatformLayer.h"
-#import "PlatformTimeRanges.h"
-#import "SecurityOrigin.h"
-#import "URL.h"
-#import "UTIUtilities.h"
-#import <objc/runtime.h>
-#import <pal/spi/mac/QTKitSPI.h>
-#import <wtf/BlockObjCExceptions.h>
-#import <wtf/NeverDestroyed.h>
-#import <wtf/SoftLinking.h>
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
-
-SOFT_LINK_FRAMEWORK(QTKit)
-
-SOFT_LINK(QTKit, QTMakeTime, QTTime, (long long timeValue, long timeScale), (timeValue, timeScale))
-
-SOFT_LINK_CLASS(QTKit, QTMovie)
-SOFT_LINK_CLASS(QTKit, QTMovieLayer)
-SOFT_LINK_CLASS(QTKit, QTUtilities)
-
-SOFT_LINK_POINTER(QTKit, QTTrackMediaTypeAttribute, NSString *)
-SOFT_LINK_POINTER(QTKit, QTMediaTypeAttribute, NSString *)
-SOFT_LINK_POINTER(QTKit, QTMediaTypeBase, NSString *)
-SOFT_LINK_POINTER(QTKit, QTMediaTypeMPEG, NSString *)
-SOFT_LINK_POINTER(QTKit, QTMediaTypeSound, NSString *)
-SOFT_LINK_POINTER(QTKit, QTMediaTypeText, NSString *)
-SOFT_LINK_POINTER(QTKit, QTMediaTypeVideo, NSString *)
-SOFT_LINK_POINTER(QTKit, QTMovieAskUnresolvedDataRefsAttribute, NSString *)
-SOFT_LINK_POINTER(QTKit, QTMovieLoopsAttribute, NSString *)
-SOFT_LINK_POINTER(QTKit, QTMovieDataAttribute, NSString *)
-SOFT_LINK_POINTER(QTKit, QTMovieDataSizeAttribute, NSString *)
-SOFT_LINK_POINTER(QTKit, QTMovieDidEndNotification, NSString *)
-SOFT_LINK_POINTER(QTKit, QTMovieHasVideoAttribute, NSString *)
-SOFT_LINK_POINTER(QTKit, QTMovieHasAudioAttribute, NSString *)
-SOFT_LINK_POINTER(QTKit, QTMovieIsActiveAttribute, NSString *)
-SOFT_LINK_POINTER(QTKit, QTMovieLoadStateAttribute, NSString *)
-SOFT_LINK_POINTER(QTKit, QTMovieLoadStateErrorAttribute, NSString *)
-SOFT_LINK_POINTER(QTKit, QTMovieLoadStateDidChangeNotification, NSString *)
-SOFT_LINK_POINTER(QTKit, QTMovieLoadedRangesDidChangeNotification, NSString *);
-SOFT_LINK_POINTER(QTKit, QTMovieNaturalSizeAttribute, NSString *)
-SOFT_LINK_POINTER(QTKit, QTMovieCurrentSizeAttribute, NSString *)
-SOFT_LINK_POINTER(QTKit, QTMoviePreventExternalURLLinksAttribute, NSString *)
-SOFT_LINK_POINTER(QTKit, QTMovieRateChangesPreservePitchAttribute, NSString *)
-SOFT_LINK_POINTER(QTKit, QTMovieRateDidChangeNotification, NSString *)
-SOFT_LINK_POINTER(QTKit, QTMovieSizeDidChangeNotification, NSString *)
-SOFT_LINK_POINTER(QTKit, QTMovieTimeDidChangeNotification, NSString *)
-SOFT_LINK_POINTER(QTKit, QTMovieTimeScaleAttribute, NSString *)
-SOFT_LINK_POINTER(QTKit, QTMovieURLAttribute, NSString *)
-SOFT_LINK_POINTER(QTKit, QTMovieVolumeDidChangeNotification, NSString *)
-SOFT_LINK_POINTER(QTKit, QTSecurityPolicyNoCrossSiteAttribute, NSString *)
-SOFT_LINK_POINTER(QTKit, QTVideoRendererWebKitOnlyNewImageAvailableNotification, NSString *)
-SOFT_LINK_POINTER(QTKit, QTMovieApertureModeClean, NSString *)
-SOFT_LINK_POINTER(QTKit, QTMovieApertureModeAttribute, NSString *)
-
-SOFT_LINK_POINTER_OPTIONAL(QTKit, QTSecurityPolicyNoLocalToRemoteSiteAttribute, NSString *)
-SOFT_LINK_POINTER_OPTIONAL(QTKit, QTSecurityPolicyNoRemoteToLocalSiteAttribute, NSString *)
-
-@interface QTMovie(WebKitExtras)
-- (QTTime)maxTimeLoaded;
-- (NSArray *)availableRanges;
-- (NSArray *)loadedRanges;
-@end
-
-#define QTMovieLayer getQTMovieLayerClass()
-#define QTUtilities getQTUtilitiesClass()
-
-#define QTTrackMediaTypeAttribute getQTTrackMediaTypeAttribute()
-#define QTMediaTypeAttribute getQTMediaTypeAttribute()
-#define QTMediaTypeBase getQTMediaTypeBase()
-#define QTMediaTypeMPEG getQTMediaTypeMPEG()
-#define QTMediaTypeSound getQTMediaTypeSound()
-#define QTMediaTypeText getQTMediaTypeText()
-#define QTMediaTypeVideo getQTMediaTypeVideo()
-#define QTMovieAskUnresolvedDataRefsAttribute getQTMovieAskUnresolvedDataRefsAttribute()
-#define QTMovieLoopsAttribute getQTMovieLoopsAttribute()
-#define QTMovieDataAttribute getQTMovieDataAttribute()
-#define QTMovieDataSizeAttribute getQTMovieDataSizeAttribute()
-#define QTMovieDidEndNotification getQTMovieDidEndNotification()
-#define QTMovieHasVideoAttribute getQTMovieHasVideoAttribute()
-#define QTMovieHasAudioAttribute getQTMovieHasAudioAttribute()
-#define QTMovieIsActiveAttribute getQTMovieIsActiveAttribute()
-#define QTMovieLoadStateAttribute getQTMovieLoadStateAttribute()
-#define QTMovieLoadStateErrorAttribute getQTMovieLoadStateErrorAttribute()
-#define QTMovieLoadStateDidChangeNotification getQTMovieLoadStateDidChangeNotification()
-#define QTMovieLoadedRangesDidChangeNotification getQTMovieLoadedRangesDidChangeNotification()
-#define QTMovieNaturalSizeAttribute getQTMovieNaturalSizeAttribute()
-#define QTMovieCurrentSizeAttribute getQTMovieCurrentSizeAttribute()
-#define QTMoviePreventExternalURLLinksAttribute getQTMoviePreventExternalURLLinksAttribute()
-#define QTMovieRateChangesPreservePitchAttribute getQTMovieRateChangesPreservePitchAttribute()
-#define QTMovieRateDidChangeNotification getQTMovieRateDidChangeNotification()
-#define QTMovieSizeDidChangeNotification getQTMovieSizeDidChangeNotification()
-#define QTMovieTimeDidChangeNotification getQTMovieTimeDidChangeNotification()
-#define QTMovieTimeScaleAttribute getQTMovieTimeScaleAttribute()
-#define QTMovieURLAttribute getQTMovieURLAttribute()
-#define QTMovieVolumeDidChangeNotification getQTMovieVolumeDidChangeNotification()
-#define QTSecurityPolicyNoCrossSiteAttribute getQTSecurityPolicyNoCrossSiteAttribute()
-#define QTSecurityPolicyNoLocalToRemoteSiteAttribute getQTSecurityPolicyNoLocalToRemoteSiteAttribute()
-#define QTSecurityPolicyNoRemoteToLocalSiteAttribute getQTSecurityPolicyNoRemoteToLocalSiteAttribute()
-#define QTVideoRendererWebKitOnlyNewImageAvailableNotification getQTVideoRendererWebKitOnlyNewImageAvailableNotification()
-#define QTMovieApertureModeClean getQTMovieApertureModeClean()
-#define QTMovieApertureModeAttribute getQTMovieApertureModeAttribute()
-
-// Older versions of the QTKit header don't have these constants.
-#if !defined QTKIT_VERSION_MAX_ALLOWED || QTKIT_VERSION_MAX_ALLOWED <= QTKIT_VERSION_7_0
-enum {
- QTMovieLoadStateError = -1L,
- QTMovieLoadStateLoaded = 2000L,
- QTMovieLoadStatePlayable = 10000L,
- QTMovieLoadStatePlaythroughOK = 20000L,
- QTMovieLoadStateComplete = 100000L
-};
-#endif
-
-using namespace WebCore;
-
-@interface WebCoreMovieObserver : NSObject
-{
- MediaPlayerPrivateQTKit* m_callback;
- BOOL m_delayCallbacks;
-}
--(id)initWithCallback:(MediaPlayerPrivateQTKit*)callback;
--(void)disconnect;
--(void)repaint;
--(void)setDelayCallbacks:(BOOL)shouldDelay;
--(void)loadStateChanged:(NSNotification *)notification;
-- (void)loadedRangesChanged:(NSNotification *)notification;
--(void)rateChanged:(NSNotification *)notification;
--(void)sizeChanged:(NSNotification *)notification;
--(void)timeChanged:(NSNotification *)notification;
--(void)didEnd:(NSNotification *)notification;
--(void)layerHostChanged:(NSNotification *)notification;
-- (void)newImageAvailable:(NSNotification *)notification;
-@end
-
-@protocol WebKitVideoRenderingDetails
--(void)setMovie:(id)movie;
--(void)drawInRect:(NSRect)rect;
-@end
-
-namespace WebCore {
-
-void MediaPlayerPrivateQTKit::registerMediaEngine(MediaEngineRegistrar registrar)
-{
- if (isAvailable())
- registrar([](MediaPlayer* player) { return std::make_unique<MediaPlayerPrivateQTKit>(player); }, getSupportedTypes,
- supportsType, originsInMediaCache, clearMediaCache, clearMediaCacheForOrigins, 0);
-}
-
-MediaPlayerPrivateQTKit::MediaPlayerPrivateQTKit(MediaPlayer* player)
- : m_player(player)
- , m_objcObserver(adoptNS([[WebCoreMovieObserver alloc] initWithCallback:this]))
- , m_seekTo(MediaTime::invalidTime())
- , m_seekTimer(*this, &MediaPlayerPrivateQTKit::seekTimerFired)
- , m_networkState(MediaPlayer::Empty)
- , m_readyState(MediaPlayer::HaveNothing)
- , m_rect()
- , m_scaleFactor(1, 1)
- , m_enabledTrackCount(0)
- , m_totalTrackCount(0)
- , m_reportedDuration(MediaTime::invalidTime())
- , m_cachedDuration(MediaTime::invalidTime())
- , m_timeToRestore(MediaTime::invalidTime())
- , m_preload(MediaPlayer::Auto)
- , m_startedPlaying(false)
- , m_isStreaming(false)
- , m_visible(false)
- , m_hasUnsupportedTracks(false)
- , m_videoFrameHasDrawn(false)
- , m_isAllowedToRender(false)
- , m_privateBrowsing(false)
-{
-}
-
-MediaPlayerPrivateQTKit::~MediaPlayerPrivateQTKit()
-{
- LOG(Media, "MediaPlayerPrivateQTKit::~MediaPlayerPrivateQTKit(%p)", this);
- tearDownVideoRendering();
-
- [[NSNotificationCenter defaultCenter] removeObserver:m_objcObserver.get()];
- [m_objcObserver.get() disconnect];
-}
-
-NSMutableDictionary *MediaPlayerPrivateQTKit::commonMovieAttributes()
-{
- NSMutableDictionary *movieAttributes = [NSMutableDictionary dictionaryWithObjectsAndKeys:
- [NSNumber numberWithBool:m_player->preservesPitch()], QTMovieRateChangesPreservePitchAttribute,
- [NSNumber numberWithBool:YES], QTMoviePreventExternalURLLinksAttribute,
- [NSNumber numberWithBool:NO], QTMovieAskUnresolvedDataRefsAttribute,
- [NSNumber numberWithBool:NO], QTMovieLoopsAttribute,
- [NSNumber numberWithBool:!m_privateBrowsing], @"QTMovieAllowPersistentCacheAttribute",
- QTMovieApertureModeClean, QTMovieApertureModeAttribute,
- nil];
-
- // Check to see if QTSecurityPolicyNoRemoteToLocalSiteAttribute is defined, which was added in QTKit 7.6.3.
- // If not, just set NoCrossSite = YES, which does the same thing as NoRemoteToLocal = YES and
- // NoLocalToRemote = YES in QTKit < 7.6.3.
- if (QTSecurityPolicyNoRemoteToLocalSiteAttribute) {
- [movieAttributes setValue:[NSNumber numberWithBool:NO] forKey:QTSecurityPolicyNoCrossSiteAttribute];
- [movieAttributes setValue:[NSNumber numberWithBool:YES] forKey:QTSecurityPolicyNoRemoteToLocalSiteAttribute];
- [movieAttributes setValue:[NSNumber numberWithBool:YES] forKey:QTSecurityPolicyNoLocalToRemoteSiteAttribute];
- } else
- [movieAttributes setValue:[NSNumber numberWithBool:YES] forKey:QTSecurityPolicyNoCrossSiteAttribute];
-
- if (m_preload < MediaPlayer::Auto)
- [movieAttributes setValue:[NSNumber numberWithBool:YES] forKey:@"QTMovieLimitReadAheadAttribute"];
-
- return movieAttributes;
-}
-
-void MediaPlayerPrivateQTKit::createQTMovie(const String& url)
-{
- URL kURL(ParsedURLString, url);
- NSURL *cocoaURL = kURL;
- NSMutableDictionary *movieAttributes = commonMovieAttributes();
- [movieAttributes setValue:cocoaURL forKey:QTMovieURLAttribute];
-
- CFDictionaryRef proxySettings = CFNetworkCopySystemProxySettings();
- CFArrayRef proxiesForURL = CFNetworkCopyProxiesForURL((CFURLRef)cocoaURL, proxySettings);
- BOOL willUseProxy = YES;
-
- if (!proxiesForURL || !CFArrayGetCount(proxiesForURL))
- willUseProxy = NO;
-
- if (CFArrayGetCount(proxiesForURL) == 1) {
- CFDictionaryRef proxy = (CFDictionaryRef)CFArrayGetValueAtIndex(proxiesForURL, 0);
- ASSERT(CFGetTypeID(proxy) == CFDictionaryGetTypeID());
-
- CFStringRef proxyType = (CFStringRef)CFDictionaryGetValue(proxy, kCFProxyTypeKey);
- ASSERT(CFGetTypeID(proxyType) == CFStringGetTypeID());
-
- if (CFStringCompare(proxyType, kCFProxyTypeNone, 0) == kCFCompareEqualTo)
- willUseProxy = NO;
- }
-
- if (!willUseProxy && !kURL.protocolIsData()) {
- // Only pass the QTMovieOpenForPlaybackAttribute flag if there are no proxy servers, due
- // to rdar://problem/7531776, or if not loading a data:// url due to rdar://problem/8103801.
- [movieAttributes setObject:[NSNumber numberWithBool:YES] forKey:@"QTMovieOpenForPlaybackAttribute"];
- }
-
- if (proxiesForURL)
- CFRelease(proxiesForURL);
- if (proxySettings)
- CFRelease(proxySettings);
-
- createQTMovie(cocoaURL, movieAttributes);
-}
-
-static void disableComponentsOnce()
-{
- static bool sComponentsDisabled = false;
- if (sComponentsDisabled)
- return;
- sComponentsDisabled = true;
-
- // eat/PDF and grip/PDF components must be disabled twice since they are registered twice
- // with different flags. However, there is currently a bug in 64-bit QTKit (<rdar://problem/8378237>)
- // which causes subsequent disable component requests of exactly the same type to be ignored if
- // QTKitServer has not yet started. As a result, we must pass in exactly the flags we want to
- // disable per component. As a failsafe, if in the future these flags change, we will disable the
- // PDF components for a third time with a wildcard flags field:
- ComponentDescription componentsToDisable[11] = {
- {'eat ', 'TEXT', 'text', 0, 0},
- {'eat ', 'TXT ', 'text', 0, 0},
- {'eat ', 'utxt', 'text', 0, 0},
- {'eat ', 'TEXT', 'tx3g', 0, 0},
- {'eat ', 'PDF ', 'vide', 0x44802, 0},
- {'eat ', 'PDF ', 'vide', 0x45802, 0},
- {'eat ', 'PDF ', 'vide', 0, 0},
- {'grip', 'PDF ', 'appl', 0x844a00, 0},
- {'grip', 'PDF ', 'appl', 0x845a00, 0},
- {'grip', 'PDF ', 'appl', 0, 0},
- {'imdc', 'pdf ', 'appl', 0, 0},
- };
-
- for (auto& component : componentsToDisable)
- [getQTMovieClass() disableComponent:component];
-}
-
-void MediaPlayerPrivateQTKit::createQTMovie(NSURL *url, NSDictionary *movieAttributes)
-{
- LOG(Media, "MediaPlayerPrivateQTKit::createQTMovie(%p) ", this);
- disableComponentsOnce();
-
- [[NSNotificationCenter defaultCenter] removeObserver:m_objcObserver.get()];
-
- bool recreating = false;
- if (m_qtMovie) {
- recreating = true;
- destroyQTVideoRenderer();
- m_qtMovie = 0;
- }
-
- // Disable rtsp streams for now, <rdar://problem/5693967>
- if (protocolIs([url scheme], "rtsp"))
- return;
-
- NSError *error = nil;
- m_qtMovie = adoptNS([allocQTMovieInstance() initWithAttributes:movieAttributes error:&error]);
-
- if (!m_qtMovie)
- return;
-
- [m_qtMovie.get() setVolume:m_player->volume()];
-
- if (recreating && hasVideo())
- createQTVideoRenderer(QTVideoRendererModeListensForNewImages);
-
- [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get()
- selector:@selector(loadStateChanged:)
- name:QTMovieLoadStateDidChangeNotification
- object:m_qtMovie.get()];
-
- // In updateState(), we track when maxTimeLoaded() == duration().
- [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get()
- selector:@selector(loadedRangesChanged:)
- name:QTMovieLoadedRangesDidChangeNotification
- object:m_qtMovie.get()];
-
- [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get()
- selector:@selector(rateChanged:)
- name:QTMovieRateDidChangeNotification
- object:m_qtMovie.get()];
- [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get()
- selector:@selector(sizeChanged:)
- name:QTMovieSizeDidChangeNotification
- object:m_qtMovie.get()];
- [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get()
- selector:@selector(timeChanged:)
- name:QTMovieTimeDidChangeNotification
- object:m_qtMovie.get()];
- [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get()
- selector:@selector(didEnd:)
- name:QTMovieDidEndNotification
- object:m_qtMovie.get()];
-}
-
-static Class QTVideoRendererClass()
-{
- static Class QTVideoRendererWebKitOnlyClass = NSClassFromString(@"QTVideoRendererWebKitOnly");
- return QTVideoRendererWebKitOnlyClass;
-}
-
-void MediaPlayerPrivateQTKit::createQTVideoRenderer(QTVideoRendererMode rendererMode)
-{
- LOG(Media, "MediaPlayerPrivateQTKit::createQTVideoRenderer(%p)", this);
- destroyQTVideoRenderer();
-
- m_qtVideoRenderer = adoptNS([[QTVideoRendererClass() alloc] init]);
- if (!m_qtVideoRenderer)
- return;
-
- // associate our movie with our instance of QTVideoRendererWebKitOnly
- [(id<WebKitVideoRenderingDetails>)m_qtVideoRenderer.get() setMovie:m_qtMovie.get()];
-
- if (rendererMode == QTVideoRendererModeListensForNewImages) {
- // listen to QTVideoRendererWebKitOnly's QTVideoRendererWebKitOnlyNewImageDidBecomeAvailableNotification
- [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get()
- selector:@selector(newImageAvailable:)
- name:QTVideoRendererWebKitOnlyNewImageAvailableNotification
- object:m_qtVideoRenderer.get()];
- }
-}
-
-void MediaPlayerPrivateQTKit::destroyQTVideoRenderer()
-{
- LOG(Media, "MediaPlayerPrivateQTKit::destroyQTVideoRenderer(%p)", this);
- if (!m_qtVideoRenderer)
- return;
-
- // stop observing the renderer's notifications before we toss it
- [[NSNotificationCenter defaultCenter] removeObserver:m_objcObserver.get()
- name:QTVideoRendererWebKitOnlyNewImageAvailableNotification
- object:m_qtVideoRenderer.get()];
-
- // disassociate our movie from our instance of QTVideoRendererWebKitOnly
- [(id<WebKitVideoRenderingDetails>)m_qtVideoRenderer.get() setMovie:nil];
-
- m_qtVideoRenderer = nil;
-}
-
-void MediaPlayerPrivateQTKit::createQTMovieLayer()
-{
- LOG(Media, "MediaPlayerPrivateQTKit::createQTMovieLayer(%p)", this);
- if (!m_qtMovie)
- return;
-
- ASSERT(supportsAcceleratedRendering());
-
- if (!m_qtVideoLayer) {
- m_qtVideoLayer = adoptNS([allocQTMovieLayerInstance() init]);
- if (!m_qtVideoLayer)
- return;
-
- [m_qtVideoLayer.get() setMovie:m_qtMovie.get()];
-#ifndef NDEBUG
- [(CALayer *)m_qtVideoLayer.get() setName:@"Video layer"];
-#endif
- // The layer will get hooked up via RenderLayerBacking::updateConfiguration().
- }
-}
-
-void MediaPlayerPrivateQTKit::destroyQTMovieLayer()
-{
- LOG(Media, "MediaPlayerPrivateQTKit::destroyQTMovieLayer(%p)", this);
- if (!m_qtVideoLayer)
- return;
-
- // disassociate our movie from our instance of QTMovieLayer
- [m_qtVideoLayer.get() setMovie:nil];
- m_qtVideoLayer = nil;
-}
-
-MediaPlayerPrivateQTKit::MediaRenderingMode MediaPlayerPrivateQTKit::currentRenderingMode() const
-{
- if (m_qtVideoLayer)
- return MediaRenderingMovieLayer;
-
- if (m_qtVideoRenderer)
- return MediaRenderingSoftwareRenderer;
-
- return MediaRenderingNone;
-}
-
-MediaPlayerPrivateQTKit::MediaRenderingMode MediaPlayerPrivateQTKit::preferredRenderingMode() const
-{
- if (!m_qtMovie)
- return MediaRenderingNone;
-
- if (supportsAcceleratedRendering() && m_player->client().mediaPlayerRenderingCanBeAccelerated(m_player))
- return MediaRenderingMovieLayer;
-
- if (!QTVideoRendererClass())
- return MediaRenderingNone;
-
- return MediaRenderingSoftwareRenderer;
-}
-
-void MediaPlayerPrivateQTKit::setUpVideoRendering()
-{
- LOG(Media, "MediaPlayerPrivateQTKit::setUpVideoRendering(%p)", this);
- if (!isReadyForVideoSetup())
- return;
-
- MediaRenderingMode currentMode = currentRenderingMode();
- MediaRenderingMode preferredMode = preferredRenderingMode();
- if (currentMode == preferredMode && currentMode != MediaRenderingNone)
- return;
-
- if (currentMode != MediaRenderingNone)
- tearDownVideoRendering();
-
- switch (preferredMode) {
- case MediaRenderingNone:
- case MediaRenderingSoftwareRenderer:
- createQTVideoRenderer(QTVideoRendererModeListensForNewImages);
- break;
- case MediaRenderingMovieLayer:
- createQTMovieLayer();
- break;
- }
-
- // If using a movie layer, inform the client so the compositing tree is updated.
- if (currentMode == MediaRenderingMovieLayer || preferredMode == MediaRenderingMovieLayer)
- m_player->client().mediaPlayerRenderingModeChanged(m_player);
-}
-
-void MediaPlayerPrivateQTKit::tearDownVideoRendering()
-{
- LOG(Media, "MediaPlayerPrivateQTKit::tearDownVideoRendering(%p)", this);
- if (m_qtVideoRenderer)
- destroyQTVideoRenderer();
- if (m_qtVideoLayer)
- destroyQTMovieLayer();
-}
-
-bool MediaPlayerPrivateQTKit::hasSetUpVideoRendering() const
-{
- return m_qtVideoLayer
- || m_qtVideoRenderer;
-}
-
-void MediaPlayerPrivateQTKit::resumeLoad()
-{
- if (!m_movieURL.isNull())
- loadInternal(m_movieURL);
-}
-
-void MediaPlayerPrivateQTKit::load(const String& url)
-{
- LOG(Media, "MediaPlayerPrivateQTKit::load(%p)", this);
- m_movieURL = url;
-
- // If the element is not supposed to load any data return immediately.
- if (m_preload == MediaPlayer::None)
- return;
-
- loadInternal(url);
-}
-
-void MediaPlayerPrivateQTKit::loadInternal(const String& url)
-{
- if (m_networkState != MediaPlayer::Loading) {
- m_networkState = MediaPlayer::Loading;
- m_player->networkStateChanged();
- }
- if (m_readyState != MediaPlayer::HaveNothing) {
- m_readyState = MediaPlayer::HaveNothing;
- m_player->readyStateChanged();
- }
- cancelSeek();
- m_videoFrameHasDrawn = false;
-
- [m_objcObserver.get() setDelayCallbacks:YES];
-
- createQTMovie(url);
-
- [m_objcObserver.get() loadStateChanged:nil];
- [m_objcObserver.get() setDelayCallbacks:NO];
-}
-
-#if ENABLE(MEDIA_SOURCE)
-void MediaPlayerPrivateQTKit::load(const String&, MediaSourcePrivateClient*)
-{
- m_networkState = MediaPlayer::FormatError;
- m_player->networkStateChanged();
-}
-#endif
-
-
-void MediaPlayerPrivateQTKit::prepareToPlay()
-{
- LOG(Media, "MediaPlayerPrivateQTKit::prepareToPlay(%p)", this);
- setPreload(MediaPlayer::Auto);
-}
-
-PlatformMedia MediaPlayerPrivateQTKit::platformMedia() const
-{
- PlatformMedia pm;
- pm.type = PlatformMedia::QTMovieType;
- pm.media.qtMovie = m_qtMovie.get();
- return pm;
-}
-
-PlatformLayer* MediaPlayerPrivateQTKit::platformLayer() const
-{
- return m_qtVideoLayer.get();
-}
-
-void MediaPlayerPrivateQTKit::play()
-{
- LOG(Media, "MediaPlayerPrivateQTKit::play(%p)", this);
- if (!metaDataAvailable())
- return;
- m_startedPlaying = true;
- [m_objcObserver.get() setDelayCallbacks:YES];
- [m_qtMovie.get() setRate:m_player->rate()];
- [m_objcObserver.get() setDelayCallbacks:NO];
-}
-
-void MediaPlayerPrivateQTKit::pause()
-{
- LOG(Media, "MediaPlayerPrivateQTKit::pause(%p)", this);
- if (!metaDataAvailable())
- return;
- m_startedPlaying = false;
- [m_objcObserver.get() setDelayCallbacks:YES];
- [m_qtMovie.get() stop];
- [m_objcObserver.get() setDelayCallbacks:NO];
-}
-
-MediaTime MediaPlayerPrivateQTKit::durationMediaTime() const
-{
- if (!metaDataAvailable())
- return MediaTime::zeroTime();
-
- if (m_cachedDuration.isValid())
- return m_cachedDuration;
-
- QTTime time = [m_qtMovie.get() duration];
- if (time.flags == kQTTimeIsIndefinite)
- return MediaTime::positiveInfiniteTime();
- return toMediaTime(time);
-}
-
-MediaTime MediaPlayerPrivateQTKit::currentMediaTime() const
-{
- if (!metaDataAvailable())
- return MediaTime::zeroTime();
- QTTime time = [m_qtMovie.get() currentTime];
- return toMediaTime(time);
-}
-
-void MediaPlayerPrivateQTKit::seek(const MediaTime& inTime)
-{
- MediaTime time = inTime;
- LOG(Media, "MediaPlayerPrivateQTKit::seek(%p) - time %s", this, toString(time).utf8().data());
- // Nothing to do if we are already in the middle of a seek to the same time.
- if (time == m_seekTo)
- return;
-
- cancelSeek();
-
- if (!metaDataAvailable())
- return;
-
- if (time > durationMediaTime())
- time = durationMediaTime();
-
- m_seekTo = time;
- if (maxMediaTimeSeekable() >= m_seekTo)
- doSeek();
- else
- m_seekTimer.start(0_s, 500_ms);
-}
-
-void MediaPlayerPrivateQTKit::doSeek()
-{
- QTTime qttime = toQTTime(m_seekTo);
- // setCurrentTime generates several event callbacks, update afterwards
- [m_objcObserver.get() setDelayCallbacks:YES];
- float oldRate = [m_qtMovie.get() rate];
-
- if (oldRate)
- [m_qtMovie.get() setRate:0];
- [m_qtMovie.get() setCurrentTime:qttime];
-
- // restore playback only if not at end, otherwise QTMovie will loop
- MediaTime timeAfterSeek = currentMediaTime();
- if (oldRate && timeAfterSeek < durationMediaTime())
- [m_qtMovie.get() setRate:oldRate];
-
- cancelSeek();
- [m_objcObserver.get() setDelayCallbacks:NO];
-}
-
-void MediaPlayerPrivateQTKit::cancelSeek()
-{
- LOG(Media, "MediaPlayerPrivateQTKit::cancelSeek(%p)", this);
- m_seekTo = MediaTime::invalidTime();
- m_seekTimer.stop();
-}
-
-void MediaPlayerPrivateQTKit::seekTimerFired()
-{
- if (!metaDataAvailable() || !seeking() || currentMediaTime() == m_seekTo) {
- cancelSeek();
- updateStates();
- m_player->timeChanged();
- return;
- }
-
- if (maxMediaTimeSeekable() >= m_seekTo)
- doSeek();
- else {
- MediaPlayer::NetworkState state = networkState();
- if (state == MediaPlayer::Empty || state == MediaPlayer::Loaded) {
- cancelSeek();
- updateStates();
- m_player->timeChanged();
- }
- }
-}
-
-bool MediaPlayerPrivateQTKit::paused() const
-{
- if (!metaDataAvailable())
- return true;
- return [m_qtMovie.get() rate] == 0;
-}
-
-bool MediaPlayerPrivateQTKit::seeking() const
-{
- if (!metaDataAvailable())
- return false;
- return m_seekTo.isValid() && m_seekTo >= MediaTime::zeroTime();
-}
-
-FloatSize MediaPlayerPrivateQTKit::naturalSize() const
-{
- if (!metaDataAvailable())
- return FloatSize();
-
- // In spite of the name of this method, return QTMovieNaturalSizeAttribute transformed by the
- // initial movie scale because the spec says intrinsic size is:
- //
- // ... the dimensions of the resource in CSS pixels after taking into account the resource's
- // dimensions, aspect ratio, clean aperture, resolution, and so forth, as defined for the
- // format used by the resource
-
- FloatSize naturalSize([[m_qtMovie.get() attributeForKey:QTMovieNaturalSizeAttribute] sizeValue]);
- if (naturalSize.isEmpty() && m_isStreaming) {
- // HTTP Live Streams will occasionally return {0,0} natural sizes while scrubbing.
- // Work around this problem (<rdar://problem/9078563>) by returning the last valid
- // cached natural size:
- naturalSize = m_cachedNaturalSize;
- } else {
- // Unfortunately, due to another QTKit bug (<rdar://problem/9082071>) we won't get a sizeChanged
- // event when this happens, so we must cache the last valid naturalSize here:
- m_cachedNaturalSize = naturalSize;
- }
-
- naturalSize.scale(m_scaleFactor.width(), m_scaleFactor.height());
- return naturalSize;
-}
-
-bool MediaPlayerPrivateQTKit::hasVideo() const
-{
- if (!metaDataAvailable())
- return false;
- return [[m_qtMovie.get() attributeForKey:QTMovieHasVideoAttribute] boolValue];
-}
-
-bool MediaPlayerPrivateQTKit::hasAudio() const
-{
- if (!m_qtMovie)
- return false;
- return [[m_qtMovie.get() attributeForKey:QTMovieHasAudioAttribute] boolValue];
-}
-
-bool MediaPlayerPrivateQTKit::supportsFullscreen() const
-{
- return true;
-}
-
-void MediaPlayerPrivateQTKit::setVolume(float volume)
-{
- LOG(Media, "MediaPlayerPrivateQTKit::setVolume(%p) - volume %f", this, volume);
- if (m_qtMovie)
- [m_qtMovie.get() setVolume:volume];
-}
-
-bool MediaPlayerPrivateQTKit::hasClosedCaptions() const
-{
- if (!metaDataAvailable())
- return false;
- return [[m_qtMovie alternateGroupTypes] containsObject:@"clcp"];
-}
-
-void MediaPlayerPrivateQTKit::setClosedCaptionsVisible(bool closedCaptionsVisible)
-{
- if (!metaDataAvailable())
- return;
-
- if (![[m_qtMovie alternateGroupTypes] containsObject:@"clcp"])
- return;
-
- NSArray *trackAlternatesArray = [m_qtMovie alternatesForMediaType:@"clcp"];
- QTTrack *track = trackAlternatesArray[0][@"QTAlternates_QTTrack"];
- if (!track)
- return;
-
- if (!closedCaptionsVisible)
- [m_qtMovie deselectAlternateGroupTrack:track];
- else
- [m_qtMovie selectAlternateGroupTrack:track];
-
- if (closedCaptionsVisible && m_qtVideoLayer) {
- // Captions will be rendered upside down unless we flag the movie as flipped (again). See <rdar://7408440>.
- [m_qtVideoLayer.get() setGeometryFlipped:YES];
- }
-}
-
-void MediaPlayerPrivateQTKit::setRate(float rate)
-{
- LOG(Media, "MediaPlayerPrivateQTKit::setRate(%p) - rate %f", this, rate);
- if (m_qtMovie)
- [m_qtMovie.get() setRate:rate];
-}
-
-double MediaPlayerPrivateQTKit::rate() const
-{
- return m_qtMovie ? [m_qtMovie rate] : 0;
-}
-
-void MediaPlayerPrivateQTKit::setPreservesPitch(bool preservesPitch)
-{
- LOG(Media, "MediaPlayerPrivateQTKit::setPreservesPitch(%p) - preservesPitch %d", this, (int)preservesPitch);
- if (!m_qtMovie)
- return;
-
- // QTMovieRateChangesPreservePitchAttribute cannot be changed dynamically after QTMovie creation.
- // If the passed in value is different than what already exists, we need to recreate the QTMovie for it to take effect.
- if ([[m_qtMovie.get() attributeForKey:QTMovieRateChangesPreservePitchAttribute] boolValue] == preservesPitch)
- return;
-
- RetainPtr<NSDictionary> movieAttributes = adoptNS([[m_qtMovie.get() movieAttributes] mutableCopy]);
- ASSERT(movieAttributes);
- [movieAttributes.get() setValue:[NSNumber numberWithBool:preservesPitch] forKey:QTMovieRateChangesPreservePitchAttribute];
- m_timeToRestore = currentMediaTime();
-
- createQTMovie([movieAttributes.get() valueForKey:QTMovieURLAttribute], movieAttributes.get());
-}
-
-std::unique_ptr<PlatformTimeRanges> MediaPlayerPrivateQTKit::buffered() const
-{
- auto timeRanges = std::make_unique<PlatformTimeRanges>();
- MediaTime loaded = maxMediaTimeLoaded();
- if (loaded > MediaTime::zeroTime())
- timeRanges->add(MediaTime::zeroTime(), loaded);
- return timeRanges;
-}
-
-static MediaTime maxValueForTimeRanges(NSArray *ranges)
-{
- if (!ranges)
- return MediaTime::zeroTime();
-
- MediaTime max;
- for (NSValue *value in ranges) {
- QTTimeRange range = [value QTTimeRangeValue];
- if (!range.time.timeScale || !range.duration.timeScale)
- continue;
-
- MediaTime time = toMediaTime(range.time);
- MediaTime duration = toMediaTime(range.duration);
- if (time.isValid() && duration.isValid())
- max = std::max(max, time + duration);
- }
-
- return max;
-}
-
-MediaTime MediaPlayerPrivateQTKit::maxMediaTimeSeekable() const
-{
- if (!metaDataAvailable())
- return MediaTime::zeroTime();
-
- // infinite duration means live stream
- if (durationMediaTime().isPositiveInfinite())
- return MediaTime::zeroTime();
-
- NSArray* seekableRanges = [m_qtMovie availableRanges];
-
- return maxValueForTimeRanges(seekableRanges);
-}
-
-MediaTime MediaPlayerPrivateQTKit::maxMediaTimeLoaded() const
-{
- if (!metaDataAvailable())
- return MediaTime::zeroTime();
- if ([m_qtMovie respondsToSelector:@selector(loadedRanges)])
- return maxValueForTimeRanges([m_qtMovie loadedRanges]);
- return toMediaTime([m_qtMovie maxTimeLoaded]);
-}
-
-bool MediaPlayerPrivateQTKit::didLoadingProgress() const
-{
- if (!duration() || !totalBytes())
- return false;
- MediaTime currentMaxTimeLoaded = maxMediaTimeLoaded();
- bool didLoadingProgress = currentMaxTimeLoaded != m_maxTimeLoadedAtLastDidLoadingProgress;
- m_maxTimeLoadedAtLastDidLoadingProgress = currentMaxTimeLoaded;
- return didLoadingProgress;
-}
-
-unsigned long long MediaPlayerPrivateQTKit::totalBytes() const
-{
- if (!metaDataAvailable())
- return 0;
- return [[m_qtMovie.get() attributeForKey:QTMovieDataSizeAttribute] longLongValue];
-}
-
-void MediaPlayerPrivateQTKit::cancelLoad()
-{
- LOG(Media, "MediaPlayerPrivateQTKit::cancelLoad(%p)", this);
- // FIXME: Is there a better way to check for this?
- if (m_networkState < MediaPlayer::Loading || m_networkState == MediaPlayer::Loaded)
- return;
-
- tearDownVideoRendering();
- m_qtMovie = nil;
-
- updateStates();
-}
-
-void MediaPlayerPrivateQTKit::cacheMovieScale()
-{
- NSSize initialSize = NSZeroSize;
- NSSize naturalSize = [[m_qtMovie.get() attributeForKey:QTMovieNaturalSizeAttribute] sizeValue];
-
- // QTMovieCurrentSizeAttribute is not allowed with instances of QTMovie that have been
- // opened with QTMovieOpenForPlaybackAttribute, so ask for the display transform attribute instead.
- NSAffineTransform *displayTransform = [m_qtMovie.get() attributeForKey:@"QTMoviePreferredTransformAttribute"];
- if (displayTransform)
- initialSize = [displayTransform transformSize:naturalSize];
- else {
- initialSize.width = naturalSize.width;
- initialSize.height = naturalSize.height;
- }
-
- if (naturalSize.width)
- m_scaleFactor.setWidth(initialSize.width / naturalSize.width);
- if (naturalSize.height)
- m_scaleFactor.setHeight(initialSize.height / naturalSize.height);
-}
-
-bool MediaPlayerPrivateQTKit::isReadyForVideoSetup() const
-{
- return m_readyState >= MediaPlayer::HaveMetadata && m_player->visible();
-}
-
-void MediaPlayerPrivateQTKit::prepareForRendering()
-{
- LOG(Media, "MediaPlayerPrivateQTKit::prepareForRendering(%p)", this);
- if (m_isAllowedToRender)
- return;
- m_isAllowedToRender = true;
-
- if (!hasSetUpVideoRendering())
- setUpVideoRendering();
-
- // If using a movie layer, inform the client so the compositing tree is updated. This is crucial if the movie
- // has a poster, as it will most likely not have a layer and we will now be rendering frames to the movie layer.
- if (currentRenderingMode() == MediaRenderingMovieLayer || preferredRenderingMode() == MediaRenderingMovieLayer)
- m_player->client().mediaPlayerRenderingModeChanged(m_player);
-}
-
-static void selectPreferredAlternateTrackForMediaType(QTMovie *movie, NSString *mediaType)
-{
- NSArray *alternates = [movie alternatesForMediaType:mediaType];
- if (!alternates.count)
- return;
-
- auto languageToQTTrackMap = adoptNS([[NSMutableDictionary alloc] initWithCapacity:alternates.count]);
-
- for (NSUInteger index = 0; index < alternates.count; ++index) {
- NSDictionary *alternateDict = alternates[index];
- NSString *languageString = alternateDict[@"QTAlternates_LanguageCodeEncoding_ISO_639_2T"];
- if (![languageString cStringUsingEncoding:kCFStringEncodingASCII])
- continue;
- if (!languageString)
- languageString = alternateDict[@"QTAlternates_LanguageCodeEncoding_RFC_4646"];
- if (!languageString) {
- LangCode langCode = [alternateDict[@"QTAlternates_LanguageCodeEncoding_MacType_LangCode"] intValue];
- auto identifier = adoptCF(CFLocaleCreateCanonicalLocaleIdentifierFromScriptManagerCodes(kCFAllocatorDefault, langCode, 0));
- languageString = (NSString *)identifier.autorelease();
- }
- if (!languageString)
- continue;
-
- id alternateTrack = alternateDict[@"QTAlternates_QTTrack"];
- if (!alternateTrack)
- continue;
-
- if (![[alternateTrack attributeForKey:@"QTTrackEnabledAttribute"] boolValue])
- continue;
-
- [languageToQTTrackMap setObject:alternateTrack forKey:languageString];
- }
-
- NSArray *preferredLanguages = [NSBundle preferredLocalizationsFromArray:[languageToQTTrackMap allKeys] forPreferences:nil];
- if (preferredLanguages.count) {
- id preferredTrack = [languageToQTTrackMap objectForKey:preferredLanguages[0]];
- if (preferredTrack) {
- // +[NSBundle preferredLocalizationsFromArray:forPreferences] may return a language which was
- // not present in preferredLanguages, and will therefore not have an associated track.
- [movie selectAlternateGroupTrack:preferredTrack];
- }
- }
-}
-
-static void selectPreferredAlternates(QTMovie *movie)
-{
- selectPreferredAlternateTrackForMediaType(movie, @"vide");
- selectPreferredAlternateTrackForMediaType(movie, @"soun");
- selectPreferredAlternateTrackForMediaType(movie, @"cplp");
- selectPreferredAlternateTrackForMediaType(movie, @"sbtl");
-}
-
-void MediaPlayerPrivateQTKit::updateStates()
-{
- MediaPlayer::NetworkState oldNetworkState = m_networkState;
- MediaPlayer::ReadyState oldReadyState = m_readyState;
-
- LOG(Media, "MediaPlayerPrivateQTKit::updateStates(%p) - entering with networkState = %i, readyState = %i", this, static_cast<int>(m_networkState), static_cast<int>(m_readyState));
-
-
- long loadState = m_qtMovie ? [[m_qtMovie.get() attributeForKey:QTMovieLoadStateAttribute] longValue] : static_cast<long>(QTMovieLoadStateError);
-
- if (loadState >= QTMovieLoadStateLoaded && m_readyState < MediaPlayer::HaveMetadata) {
- disableUnsupportedTracks();
- if (m_player->inMediaDocument()) {
- if (!m_enabledTrackCount || m_hasUnsupportedTracks) {
- // This has a type of media that we do not handle directly with a <video>
- // element, eg. a rtsp track or QuickTime VR. Tell the MediaPlayerClient
- // that we noticed.
- sawUnsupportedTracks();
- return;
- }
- } else if (!m_enabledTrackCount)
- loadState = QTMovieLoadStateError;
-
- if (loadState != QTMovieLoadStateError) {
- selectPreferredAlternates(m_qtMovie.get());
- cacheMovieScale();
- MediaPlayer::MovieLoadType movieType = movieLoadType();
- m_isStreaming = movieType == MediaPlayer::StoredStream || movieType == MediaPlayer::LiveStream;
- }
- }
-
- // If this movie is reloading and we mean to restore the current time/rate, this might be the right time to do it.
- if (loadState >= QTMovieLoadStateLoaded && oldNetworkState < MediaPlayer::Loaded && m_timeToRestore.isValid()) {
- QTTime qttime = toQTTime(m_timeToRestore);
- m_timeToRestore = MediaTime::invalidTime();
-
- // Disable event callbacks from setCurrentTime for restoring time in a recreated video
- [m_objcObserver.get() setDelayCallbacks:YES];
- [m_qtMovie.get() setCurrentTime:qttime];
- [m_qtMovie.get() setRate:m_player->rate()];
- [m_objcObserver.get() setDelayCallbacks:NO];
- }
-
- BOOL completelyLoaded = !m_isStreaming && (loadState >= QTMovieLoadStateComplete);
-
- // Note: QT indicates that we are fully loaded with QTMovieLoadStateComplete.
- // However newer versions of QT do not, so we check maxTimeLoaded against duration.
- if (!completelyLoaded && !m_isStreaming && metaDataAvailable())
- completelyLoaded = maxMediaTimeLoaded() == durationMediaTime();
-
- if (completelyLoaded) {
- // "Loaded" is reserved for fully buffered movies, never the case when streaming
- m_networkState = MediaPlayer::Loaded;
- m_readyState = MediaPlayer::HaveEnoughData;
- } else if (loadState >= QTMovieLoadStatePlaythroughOK) {
- m_readyState = MediaPlayer::HaveEnoughData;
- m_networkState = MediaPlayer::Loading;
- } else if (loadState >= QTMovieLoadStatePlayable) {
- // FIXME: This might not work correctly in streaming case, <rdar://problem/5693967>
- m_readyState = currentMediaTime() < maxMediaTimeLoaded() ? MediaPlayer::HaveFutureData : MediaPlayer::HaveCurrentData;
- m_networkState = MediaPlayer::Loading;
- } else if (loadState >= QTMovieLoadStateLoaded) {
- m_readyState = MediaPlayer::HaveMetadata;
- m_networkState = MediaPlayer::Loading;
- } else if (loadState > QTMovieLoadStateError) {
- m_readyState = MediaPlayer::HaveNothing;
- m_networkState = MediaPlayer::Loading;
- } else {
- // Loading or decoding failed.
-
- if (m_player->inMediaDocument()) {
- // Something went wrong in the loading of media within a standalone file.
- // This can occur with chained refmovies pointing to streamed media.
- sawUnsupportedTracks();
- return;
- }
-
- MediaTime loaded = maxMediaTimeLoaded();
- if (!loaded)
- m_readyState = MediaPlayer::HaveNothing;
-
- if (!m_enabledTrackCount)
- m_networkState = MediaPlayer::FormatError;
- else {
- // FIXME: We should differentiate between load/network errors and decode errors <rdar://problem/5605692>
- if (loaded > MediaTime::zeroTime())
- m_networkState = MediaPlayer::DecodeError;
- else
- m_readyState = MediaPlayer::HaveNothing;
- }
- }
-
- if (isReadyForVideoSetup() && !hasSetUpVideoRendering())
- setUpVideoRendering();
-
- if (seeking())
- m_readyState = m_readyState >= MediaPlayer::HaveMetadata ? MediaPlayer::HaveMetadata : MediaPlayer::HaveNothing;
-
- // Streaming movies don't use the network when paused.
- if (m_isStreaming && m_readyState >= MediaPlayer::HaveMetadata && m_networkState >= MediaPlayer::Loading && [m_qtMovie.get() rate] == 0)
- m_networkState = MediaPlayer::Idle;
-
- if (m_networkState != oldNetworkState)
- m_player->networkStateChanged();
-
- if (m_readyState != oldReadyState)
- m_player->readyStateChanged();
-
- if (loadState >= QTMovieLoadStateLoaded) {
- MediaTime dur = durationMediaTime();
- if (dur != m_reportedDuration) {
- if (m_reportedDuration.isValid())
- m_player->durationChanged();
- m_reportedDuration = dur;
- }
- }
-
- LOG(Media, "MediaPlayerPrivateQTKit::updateStates(%p) - exiting with networkState = %i, readyState = %i", this, static_cast<int>(m_networkState), static_cast<int>(m_readyState));
-}
-
-long MediaPlayerPrivateQTKit::platformErrorCode() const
-{
- if (!m_qtMovie)
- return 0;
-
- NSError* error = (NSError*)[m_qtMovie attributeForKey:QTMovieLoadStateErrorAttribute];
- if (!error || ![error isKindOfClass:[NSError class]])
- return 0;
-
- return [error code];
-}
-
-void MediaPlayerPrivateQTKit::loadStateChanged()
-{
- LOG(Media, "MediaPlayerPrivateQTKit::loadStateChanged(%p) - loadState = %li", this, [[m_qtMovie.get() attributeForKey:QTMovieLoadStateAttribute] longValue]);
-
- if (!m_hasUnsupportedTracks)
- updateStates();
-}
-
-void MediaPlayerPrivateQTKit::loadedRangesChanged()
-{
- LOG(Media, "MediaPlayerPrivateQTKit::loadedRangesChanged(%p) - loadState = %li", this, [[m_qtMovie.get() attributeForKey:QTMovieLoadStateAttribute] longValue]);
-
- if (!m_hasUnsupportedTracks)
- updateStates();
-}
-
-void MediaPlayerPrivateQTKit::rateChanged()
-{
- LOG(Media, "MediaPlayerPrivateQTKit::rateChanged(%p) - rate = %li", this, [m_qtMovie.get() rate]);
- if (m_hasUnsupportedTracks)
- return;
-
- updateStates();
- m_player->rateChanged();
-}
-
-void MediaPlayerPrivateQTKit::sizeChanged()
-{
- LOG(Media, "MediaPlayerPrivateQTKit::sizeChanged(%p)", this);
- if (!m_hasUnsupportedTracks)
- m_player->sizeChanged();
-}
-
-void MediaPlayerPrivateQTKit::timeChanged()
-{
- LOG(Media, "MediaPlayerPrivateQTKit::timeChanged(%p)", this);
- if (m_hasUnsupportedTracks)
- return;
-
- // It may not be possible to seek to a specific time in a streamed movie. When seeking in a
- // stream QuickTime sets the movie time to closest time possible and posts a timechanged
- // notification. Update m_seekTo so we can detect when the seek completes.
- if (m_seekTo.isValid())
- m_seekTo = currentMediaTime();
-
- m_timeToRestore = MediaTime::invalidTime();
- updateStates();
- m_player->timeChanged();
-}
-
-void MediaPlayerPrivateQTKit::didEnd()
-{
- LOG(Media, "MediaPlayerPrivateQTKit::didEnd(%p)", this);
- if (m_hasUnsupportedTracks)
- return;
-
- m_startedPlaying = false;
-
- // Hang onto the current time and use it as duration from now on since QuickTime is telling us we
- // are at the end. Do this because QuickTime sometimes reports one time for duration and stops
- // playback at another time, which causes problems in HTMLMediaElement. QTKit's 'ended' event
- // fires when playing in reverse so don't update duration when at time zero!
- MediaTime now = currentMediaTime();
- if (now > MediaTime::zeroTime())
- m_cachedDuration = now;
-
- updateStates();
- m_player->timeChanged();
-}
-
-void MediaPlayerPrivateQTKit::layerHostChanged(PlatformLayer* rootLayer)
-{
- UNUSED_PARAM(rootLayer);
-}
-
-void MediaPlayerPrivateQTKit::setSize(const IntSize&)
-{
-}
-
-void MediaPlayerPrivateQTKit::setVisible(bool b)
-{
- if (m_visible != b) {
- m_visible = b;
- if (b)
- setUpVideoRendering();
- else
- tearDownVideoRendering();
- }
-}
-
-bool MediaPlayerPrivateQTKit::hasAvailableVideoFrame() const
-{
- // When using a QTMovieLayer return true as soon as the movie reaches QTMovieLoadStatePlayable
- // because although we don't *know* when the first frame has decoded, by the time we get and
- // process the notification a frame should have propagated the VisualContext and been set on
- // the layer.
- if (currentRenderingMode() == MediaRenderingMovieLayer)
- return m_readyState >= MediaPlayer::HaveCurrentData;
-
- // When using the software renderer QuickTime signals that a frame is available so we might as well
- // wait until we know that a frame has been drawn.
- return m_videoFrameHasDrawn;
-}
-
-void MediaPlayerPrivateQTKit::repaint()
-{
- if (m_hasUnsupportedTracks)
- return;
-
- m_videoFrameHasDrawn = true;
- m_player->repaint();
-}
-
-void MediaPlayerPrivateQTKit::paintCurrentFrameInContext(GraphicsContext& context, const FloatRect& r)
-{
- id qtVideoRenderer = m_qtVideoRenderer.get();
- if (!qtVideoRenderer && currentRenderingMode() == MediaRenderingMovieLayer) {
- // We're being told to render into a context, but we already have the
- // MovieLayer going. This probably means we've been called from <canvas>.
- // Set up a QTVideoRenderer to use, but one that doesn't register for
- // update callbacks. That way, it won't bother us asking to repaint.
- createQTVideoRenderer(QTVideoRendererModeDefault);
- }
- paint(context, r);
-}
-
-void MediaPlayerPrivateQTKit::paint(GraphicsContext& context, const FloatRect& r)
-{
- if (context.paintingDisabled() || m_hasUnsupportedTracks)
- return;
- id qtVideoRenderer = m_qtVideoRenderer.get();
- if (!qtVideoRenderer)
- return;
-
- [m_objcObserver.get() setDelayCallbacks:YES];
- BEGIN_BLOCK_OBJC_EXCEPTIONS;
- NSGraphicsContext* newContext;
- FloatSize scaleFactor(1.0f, -1.0f);
- FloatRect paintRect(FloatPoint(), r.size());
-
- GraphicsContextStateSaver stateSaver(context);
- context.translate(r.x(), r.y() + r.height());
- context.scale(scaleFactor);
- context.setImageInterpolationQuality(InterpolationLow);
-
- newContext = [NSGraphicsContext graphicsContextWithGraphicsPort:context.platformContext() flipped:NO];
-
- [NSGraphicsContext saveGraphicsState];
- [NSGraphicsContext setCurrentContext:newContext];
- [(id<WebKitVideoRenderingDetails>)qtVideoRenderer drawInRect:paintRect];
- [NSGraphicsContext restoreGraphicsState];
-
- END_BLOCK_OBJC_EXCEPTIONS;
- [m_objcObserver.get() setDelayCallbacks:NO];
-}
-
-static bool shouldRejectMIMEType(const String& type)
-{
- // QTKit will return non-video MIME types which it claims to support, but which we
- // do not support in the <video> element. Disclaim all non video/ or audio/ types.
- return !startsWithLettersIgnoringASCIICase(type, "video/") && !startsWithLettersIgnoringASCIICase(type, "audio/");
-}
-
-static HashSet<String, ASCIICaseInsensitiveHash> createFileTypesSet(NSArray *fileTypes)
-{
- HashSet<String, ASCIICaseInsensitiveHash> set;
- for (NSString *fileType in fileTypes) {
- CFStringRef ext = reinterpret_cast<CFStringRef>(fileType);
- auto uti = adoptCF(UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, ext, NULL));
- if (!uti)
- continue;
- auto mime = MIMETypeFromUTI(uti.get());
- if (shouldRejectMIMEType(mime))
- continue;
- if (!mime.isEmpty())
- set.add(mime);
-
- // -movieFileTypes: returns both file extensions and OSTypes. The later are surrounded by single
- // quotes, eg. 'MooV', so don't bother looking at those.
- if (CFStringGetCharacterAtIndex(ext, 0) != '\'') {
- // UTI is missing many media related MIME types supported by QTKit (see rdar://6434168), and not all
- // web servers use the MIME type UTI returns for an extension (see rdar://7875393), so even if UTI
- // has a type for this extension add any types in hard coded table in the MIME type registry.
- for (auto& type : MIMETypeRegistry::getMediaMIMETypesForExtension(ext)) {
- if (!shouldRejectMIMEType(type))
- set.add(type);
- }
- }
- }
- return set;
-}
-
-static const HashSet<String, ASCIICaseInsensitiveHash>& mimeCommonTypesCache()
-{
- static const auto cache = makeNeverDestroyed(createFileTypesSet([getQTMovieClass() movieFileTypes:QTIncludeCommonTypes]));
- return cache;
-}
-
-static const HashSet<String, ASCIICaseInsensitiveHash>& mimeModernTypesCache()
-{
- static const auto cache = makeNeverDestroyed(createFileTypesSet([getQTMovieClass() movieFileTypes:(QTMovieFileTypeOptions)QTIncludeOnlyFigMediaFileTypes]));
- return cache;
-}
-
-static void concatenateHashSets(HashSet<String, ASCIICaseInsensitiveHash>& destination, const HashSet<String, ASCIICaseInsensitiveHash>& source)
-{
- for (auto& type : source)
- destination.add(type);
-}
-
-void MediaPlayerPrivateQTKit::getSupportedTypes(HashSet<String, ASCIICaseInsensitiveHash>& supportedTypes)
-{
- concatenateHashSets(supportedTypes, mimeModernTypesCache());
-
- // Note: this method starts QTKitServer if it isn't already running when in 64-bit because it has to return the list
- // of every MIME type supported by QTKit.
- concatenateHashSets(supportedTypes, mimeCommonTypesCache());
-}
-
-MediaPlayer::SupportsType MediaPlayerPrivateQTKit::supportsType(const MediaEngineSupportParameters& parameters)
-{
-#if ENABLE(MEDIA_SOURCE)
- if (parameters.isMediaSource)
- return MediaPlayer::IsNotSupported;
-#endif
-
- // Only return "IsSupported" if there is no codecs parameter for now as there is no way to ask QT if it supports an
- // extended MIME type yet.
-
- // Due to <rdar://problem/10777059>, avoid calling the mime types cache functions if at
- // all possible:
- auto containerType = parameters.type.containerType();
- if (shouldRejectMIMEType(containerType))
- return MediaPlayer::IsNotSupported;
-
- // We check the "modern" type cache first, as it doesn't require QTKitServer to start.
- if (mimeModernTypesCache().contains(containerType) || mimeCommonTypesCache().contains(containerType))
- return parameters.type.codecs().isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported;
-
- return MediaPlayer::IsNotSupported;
-}
-
-bool MediaPlayerPrivateQTKit::isAvailable()
-{
- // On 10.5 and higher, QuickTime will always be new enough for <video> and <audio> support, so we just check that the framework can be loaded.
- return QTKitLibrary();
-}
-
-HashSet<RefPtr<SecurityOrigin>> MediaPlayerPrivateQTKit::originsInMediaCache(const String&)
-{
- HashSet<RefPtr<SecurityOrigin>> origins;
- NSArray *mediaSites = [[QTUtilities qtUtilities] sitesInDownloadCache];
-
- for (NSString *site in mediaSites) {
- URL siteAsURL = URL(URL(), site);
- if (siteAsURL.isValid())
- origins.add(SecurityOrigin::create(siteAsURL));
- }
- return origins;
-}
-
-void MediaPlayerPrivateQTKit::clearMediaCache(const String&, WallTime)
-{
- LOG(Media, "MediaPlayerPrivateQTKit::clearMediaCache()");
- [[QTUtilities qtUtilities] clearDownloadCache];
-}
-
-void MediaPlayerPrivateQTKit::clearMediaCacheForOrigins(const String&, const HashSet<RefPtr<SecurityOrigin>>& origins)
-{
- LOG(Media, "MediaPlayerPrivateQTKit::clearMediaCacheForOrigins()");
- for (auto& origin : origins)
- [[QTUtilities qtUtilities] clearDownloadCacheForSite:origin->toRawString()];
-}
-
-void MediaPlayerPrivateQTKit::disableUnsupportedTracks()
-{
- LOG(Media, "MediaPlayerPrivateQTKit::disableUnsupportedTracks(%p)", this);
-
- if (!m_qtMovie) {
- m_enabledTrackCount = 0;
- m_totalTrackCount = 0;
- return;
- }
-
- static NeverDestroyed<HashSet<String>> allowedTrackTypes = [] {
- static NSString * const types[] = {
- QTMediaTypeVideo,
- QTMediaTypeSound,
- QTMediaTypeText,
- QTMediaTypeBase,
- QTMediaTypeMPEG,
- @"clcp", // Closed caption
- @"sbtl", // Subtitle
- @"odsm", // MPEG-4 object descriptor stream
- @"sdsm", // MPEG-4 scene description stream
- @"tmcd", // timecode
- @"tc64", // timcode-64
- @"tmet", // timed metadata
- };
- HashSet<String> set;
- for (auto& type : types)
- set.add(type);
- return set;
- }();
-
- NSArray *tracks = [m_qtMovie.get() tracks];
-
- m_totalTrackCount = [tracks count];
- m_enabledTrackCount = m_totalTrackCount;
- for (unsigned trackIndex = 0; trackIndex < m_totalTrackCount; trackIndex++) {
- // Grab the track at the current index. If there isn't one there, then
- // we can move onto the next one.
- QTTrack *track = [tracks objectAtIndex:trackIndex];
- if (!track)
- continue;
-
- // Check to see if the track is disabled already, we should move along.
- // We don't need to re-disable it.
- if (![track isEnabled]) {
- --m_enabledTrackCount;
- continue;
- }
-
- // Get the track's media type.
- NSString *mediaType = [track attributeForKey:QTTrackMediaTypeAttribute];
- if (!mediaType)
- continue;
-
- // Test whether the media type is in our white list.
- if (!allowedTrackTypes.get().contains(mediaType)) {
- // If this track type is not allowed, then we need to disable it.
- [track setEnabled:NO];
- --m_enabledTrackCount;
- m_hasUnsupportedTracks = true;
- }
-
- // Disable chapter tracks. These are most likely to lead to trouble, as
- // they will be composited under the video tracks, forcing QT to do extra
- // work.
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wundeclared-selector"
- QTTrack *chapterTrack = [track performSelector:@selector(chapterlist)];
-#pragma clang diagnostic pop
- if (!chapterTrack)
- continue;
-
- // Try to grab the media for the track.
- QTMedia *chapterMedia = [chapterTrack media];
- if (!chapterMedia)
- continue;
-
- // Grab the media type for this track.
- id chapterMediaType = [chapterMedia attributeForKey:QTMediaTypeAttribute];
- if (!chapterMediaType)
- continue;
-
- // Check to see if the track is a video track. We don't care about
- // other non-video tracks.
- if (![chapterMediaType isEqual:QTMediaTypeVideo])
- continue;
-
- // Check to see if the track is already disabled. If it is, we
- // should move along.
- if (![chapterTrack isEnabled])
- continue;
-
- // Disable the evil, evil track.
- [chapterTrack setEnabled:NO];
- --m_enabledTrackCount;
- m_hasUnsupportedTracks = true;
- }
-}
-
-void MediaPlayerPrivateQTKit::sawUnsupportedTracks()
-{
- m_hasUnsupportedTracks = true;
- m_player->client().mediaPlayerSawUnsupportedTracks(m_player);
-}
-
-bool MediaPlayerPrivateQTKit::supportsAcceleratedRendering() const
-{
- return isReadyForVideoSetup() && getQTMovieLayerClass() != Nil;
-}
-
-void MediaPlayerPrivateQTKit::acceleratedRenderingStateChanged()
-{
- // Set up or change the rendering path if necessary.
- setUpVideoRendering();
-}
-
-bool MediaPlayerPrivateQTKit::hasSingleSecurityOrigin() const
-{
- if (!m_qtMovie)
- return false;
-
- Ref<SecurityOrigin> resolvedOrigin = SecurityOrigin::create(URL([m_qtMovie URL]));
- Ref<SecurityOrigin> requestedOrigin = SecurityOrigin::createFromString(m_movieURL);
- return resolvedOrigin->isSameSchemeHostPort(requestedOrigin.get());
-}
-
-MediaPlayer::MovieLoadType MediaPlayerPrivateQTKit::movieLoadType() const
-{
- if (!m_qtMovie)
- return MediaPlayer::MovieLoadType::Unknown;
-
- UInt32 movieType = [m_qtMovie movieType];
- switch (movieType) {
- case QTMovieTypeLocal:
- case QTMovieTypeFastStart:
- return MediaPlayer::MovieLoadType::Download;
- case QTMovieTypeLiveStream:
- return MediaPlayer::MovieLoadType::LiveStream;
- case QTMovieTypeStoredStream:
- return MediaPlayer::MovieLoadType::StoredStream;
- case QTMovieTypeUnknown:
- default:
- return MediaPlayer::MovieLoadType::Unknown;
- }
-}
-
-void MediaPlayerPrivateQTKit::setPreload(MediaPlayer::Preload preload)
-{
- m_preload = preload;
- if (m_preload == MediaPlayer::None)
- return;
-
- if (!m_qtMovie)
- resumeLoad();
- else if (m_preload == MediaPlayer::Auto)
- [m_qtMovie.get() setAttribute:[NSNumber numberWithBool:NO] forKey:@"QTMovieLimitReadAheadAttribute"];
-}
-
-void MediaPlayerPrivateQTKit::setPrivateBrowsingMode(bool privateBrowsing)
-{
- m_privateBrowsing = privateBrowsing;
- if (!m_qtMovie)
- return;
- [m_qtMovie.get() setAttribute:[NSNumber numberWithBool:!privateBrowsing] forKey:@"QTMovieAllowPersistentCacheAttribute"];
-}
-
-bool MediaPlayerPrivateQTKit::canSaveMediaData() const
-{
- URL url;
-
- if (durationMediaTime().isPositiveInfinite())
- return false;
-
- if (m_qtMovie)
- url = "" URL]);
- else
- url = "" m_movieURL);
-
- if (url.isLocalFile())
- return true;
-
- if (url.protocolIsInHTTPFamily())
- return true;
-
- return false;
-}
-
-#if ENABLE(WIRELESS_PLAYBACK_TARGET)
-void MediaPlayerPrivateQTKit::setWirelessPlaybackTarget(Ref<MediaPlaybackTarget>&& target)
-{
- m_playbackTarget = WTFMove(target);
-}
-
-void MediaPlayerPrivateQTKit::setShouldPlayToPlaybackTarget(bool shouldPlayToTarget)
-{
- if (shouldPlayToTarget == m_shouldPlayToTarget)
- return;
-
- m_shouldPlayToTarget = shouldPlayToTarget;
-
- if (m_player)
- m_player->currentPlaybackTargetIsWirelessChanged();
-}
-
-bool MediaPlayerPrivateQTKit::isCurrentPlaybackTargetWireless() const
-{
- if (!m_playbackTarget)
- return false;
-
- return m_shouldPlayToTarget && m_playbackTarget->hasActiveRoute();
-}
-#endif
-
-} // namespace WebCore
-
-@implementation WebCoreMovieObserver
-
-- (id)initWithCallback:(MediaPlayerPrivateQTKit*)callback
-{
- m_callback = callback;
- return [super init];
-}
-
-- (void)disconnect
-{
- [NSObject cancelPreviousPerformRequestsWithTarget:self];
- m_callback = 0;
-}
-
--(void)repaint
-{
- if (m_delayCallbacks)
- [self performSelector:_cmd withObject:nil afterDelay:0.];
- else if (m_callback)
- m_callback->repaint();
-}
-
-- (void)loadStateChanged:(NSNotification *)unusedNotification
-{
- UNUSED_PARAM(unusedNotification);
- if (m_delayCallbacks)
- [self performSelector:_cmd withObject:nil afterDelay:0];
- else
- m_callback->loadStateChanged();
-}
-
-- (void)loadedRangesChanged:(NSNotification *)unusedNotification
-{
- UNUSED_PARAM(unusedNotification);
- if (m_delayCallbacks)
- [self performSelector:_cmd withObject:nil afterDelay:0];
- else
- m_callback->loadedRangesChanged();
-}
-
-- (void)rateChanged:(NSNotification *)unusedNotification
-{
- UNUSED_PARAM(unusedNotification);
- if (m_delayCallbacks)
- [self performSelector:_cmd withObject:nil afterDelay:0];
- else
- m_callback->rateChanged();
-}
-
-- (void)sizeChanged:(NSNotification *)unusedNotification
-{
- UNUSED_PARAM(unusedNotification);
- if (m_delayCallbacks)
- [self performSelector:_cmd withObject:nil afterDelay:0];
- else
- m_callback->sizeChanged();
-}
-
-- (void)timeChanged:(NSNotification *)unusedNotification
-{
- UNUSED_PARAM(unusedNotification);
- if (m_delayCallbacks)
- [self performSelector:_cmd withObject:nil afterDelay:0];
- else
- m_callback->timeChanged();
-}
-
-- (void)didEnd:(NSNotification *)unusedNotification
-{
- UNUSED_PARAM(unusedNotification);
- if (m_delayCallbacks)
- [self performSelector:_cmd withObject:nil afterDelay:0];
- else
- m_callback->didEnd();
-}
-
-- (void)newImageAvailable:(NSNotification *)unusedNotification
-{
- UNUSED_PARAM(unusedNotification);
- [self repaint];
-}
-
-- (void)layerHostChanged:(NSNotification *)notification
-{
- CALayer* rootLayer = static_cast<CALayer*>([notification object]);
- m_callback->layerHostChanged(rootLayer);
-}
-
-- (void)setDelayCallbacks:(BOOL)shouldDelay
-{
- m_delayCallbacks = shouldDelay;
-}
-
-@end
-
-#pragma clang diagnostic pop // deprecated-declarations
-
-#endif