MediaPlayerPrivateQuickTimeVisualContext.cpp [plain text]
#include "config.h"
#if ENABLE(VIDEO)
#include "MediaPlayerPrivateQuickTimeVisualContext.h"
#include "Cookie.h"
#include "CookieJar.h"
#include "Frame.h"
#include "FrameView.h"
#include "GraphicsContext.h"
#include "KURL.h"
#include "MediaPlayerPrivateTaskTimer.h"
#include "QTCFDictionary.h"
#include "QTMovie.h"
#include "QTMovieTask.h"
#include "QTMovieVisualContext.h"
#include "ScrollView.h"
#include "Settings.h"
#include "SoftLinking.h"
#include "StringBuilder.h"
#include "StringHash.h"
#include "TimeRanges.h"
#include "Timer.h"
#include <CoreGraphics/CGContext.h>
#include <Wininet.h>
#include <wtf/CurrentTime.h>
#include <wtf/HashSet.h>
#include <wtf/MainThread.h>
#include <wtf/MathExtras.h>
#include <wtf/StdLibExtras.h>
#if USE(ACCELERATED_COMPOSITING)
#include "GraphicsLayerCACF.h"
#include "WKCACFLayer.h"
#include "WKCAImageQueue.h"
#endif
using namespace std;
namespace WebCore {
static CGImageRef CreateCGImageFromPixelBuffer(QTPixelBuffer buffer);
static bool requiredDllsAvailable();
SOFT_LINK_LIBRARY(Wininet)
SOFT_LINK(Wininet, InternetSetCookieExW, DWORD, WINAPI, (LPCWSTR lpszUrl, LPCWSTR lpszCookieName, LPCWSTR lpszCookieData, DWORD dwFlags, DWORD_PTR dwReserved), (lpszUrl, lpszCookieName, lpszCookieData, dwFlags, dwReserved))
class MediaPlayerPrivateQuickTimeVisualContext::MovieClient : public QTMovieClient {
public:
MovieClient(MediaPlayerPrivateQuickTimeVisualContext* parent) : m_parent(parent) {}
virtual ~MovieClient() { m_parent = 0; }
virtual void movieEnded(QTMovie*);
virtual void movieLoadStateChanged(QTMovie*);
virtual void movieTimeChanged(QTMovie*);
private:
MediaPlayerPrivateQuickTimeVisualContext* m_parent;
};
#if USE(ACCELERATED_COMPOSITING)
class MediaPlayerPrivateQuickTimeVisualContext::LayerClient : public GraphicsLayerClient {
public:
LayerClient(MediaPlayerPrivateQuickTimeVisualContext* parent) : m_parent(parent) {}
virtual ~LayerClient() { m_parent = 0; }
virtual void paintContents(const GraphicsLayer*, GraphicsContext&, GraphicsLayerPaintingPhase, const IntRect& inClip);
virtual void notifyAnimationStarted(const GraphicsLayer*, double time) { }
virtual void notifySyncRequired(const GraphicsLayer*) { }
virtual bool showDebugBorders() const { return false; }
virtual bool showRepaintCounter() const { return false; }
private:
MediaPlayerPrivateQuickTimeVisualContext* m_parent;
};
#endif
class MediaPlayerPrivateQuickTimeVisualContext::VisualContextClient : public QTMovieVisualContextClient {
public:
VisualContextClient(MediaPlayerPrivateQuickTimeVisualContext* parent) : m_parent(parent) {}
virtual ~VisualContextClient() { m_parent = 0; }
void imageAvailableForTime(const QTCVTimeStamp*);
static void retrieveCurrentImageProc(void*);
private:
MediaPlayerPrivateQuickTimeVisualContext* m_parent;
};
MediaPlayerPrivateInterface* MediaPlayerPrivateQuickTimeVisualContext::create(MediaPlayer* player)
{
return new MediaPlayerPrivateQuickTimeVisualContext(player);
}
void MediaPlayerPrivateQuickTimeVisualContext::registerMediaEngine(MediaEngineRegistrar registrar)
{
if (isAvailable())
registrar(create, getSupportedTypes, supportsType);
}
MediaPlayerPrivateQuickTimeVisualContext::MediaPlayerPrivateQuickTimeVisualContext(MediaPlayer* player)
: m_player(player)
, m_seekTo(-1)
, m_seekTimer(this, &MediaPlayerPrivateQuickTimeVisualContext::seekTimerFired)
, m_visualContextTimer(this, &MediaPlayerPrivateQuickTimeVisualContext::visualContextTimerFired)
, m_networkState(MediaPlayer::Empty)
, m_readyState(MediaPlayer::HaveNothing)
, m_enabledTrackCount(0)
, m_totalTrackCount(0)
, m_hasUnsupportedTracks(false)
, m_startedPlaying(false)
, m_isStreaming(false)
, m_visible(false)
, m_newFrameAvailable(false)
, m_movieClient(new MediaPlayerPrivateQuickTimeVisualContext::MovieClient(this))
#if USE(ACCELERATED_COMPOSITING)
, m_layerClient(new MediaPlayerPrivateQuickTimeVisualContext::LayerClient(this))
#endif
, m_visualContextClient(new MediaPlayerPrivateQuickTimeVisualContext::VisualContextClient(this))
{
}
MediaPlayerPrivateQuickTimeVisualContext::~MediaPlayerPrivateQuickTimeVisualContext()
{
tearDownVideoRendering();
cancelCallOnMainThread(&VisualContextClient::retrieveCurrentImageProc, this);
}
bool MediaPlayerPrivateQuickTimeVisualContext::supportsFullscreen() const
{
#if USE(ACCELERATED_COMPOSITING)
Document* document = m_player->mediaPlayerClient()->mediaPlayerOwningDocument();
if (document && document->settings())
return document->settings()->acceleratedCompositingEnabled();
#endif
return false;
}
PlatformMedia MediaPlayerPrivateQuickTimeVisualContext::platformMedia() const
{
PlatformMedia p;
p.type = PlatformMedia::QTMovieVisualContextType;
p.media.qtMovieVisualContext = m_visualContext.get();
return p;
}
#if USE(ACCELERATED_COMPOSITING)
PlatformLayer* MediaPlayerPrivateQuickTimeVisualContext::platformLayer() const
{
return m_qtVideoLayer ? m_qtVideoLayer->platformLayer() : 0;
}
#endif
String MediaPlayerPrivateQuickTimeVisualContext::rfc2616DateStringFromTime(CFAbsoluteTime time)
{
static const char* const dayStrings[] = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };
static const char* const monthStrings[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
static const CFStringRef dateFormatString = CFSTR("%s, %02d %s %04d %02d:%02d:%02d GMT");
static CFTimeZoneRef gmtTimeZone;
if (!gmtTimeZone)
gmtTimeZone = CFTimeZoneCopyDefault();
CFGregorianDate dateValue = CFAbsoluteTimeGetGregorianDate(time, gmtTimeZone);
if (!CFGregorianDateIsValid(dateValue, kCFGregorianAllUnits))
return String();
time = CFGregorianDateGetAbsoluteTime(dateValue, gmtTimeZone);
SInt32 day = CFAbsoluteTimeGetDayOfWeek(time, 0);
RetainPtr<CFStringRef> dateCFString(AdoptCF, CFStringCreateWithFormat(0, 0, dateFormatString, dayStrings[day - 1], dateValue.day,
monthStrings[dateValue.month - 1], dateValue.year, dateValue.hour, dateValue.minute, (int)dateValue.second));
return dateCFString.get();
}
static void addCookieParam(StringBuilder& cookieBuilder, const String& name, const String& value)
{
if (name.isEmpty())
return;
if (cookieBuilder.length())
cookieBuilder.append("; ");
cookieBuilder.append(name);
if (!value.isEmpty()) {
cookieBuilder.append("=");
cookieBuilder.append(value);
}
}
void MediaPlayerPrivateQuickTimeVisualContext::setUpCookiesForQuickTime(const String& url)
{
Frame* frame = m_player->frameView() ? m_player->frameView()->frame() : 0;
if (!frame || !frame->page() || !frame->page()->cookieEnabled())
return;
KURL movieURL = KURL(KURL(), url);
Vector<Cookie> documentCookies;
if (!getRawCookies(frame->document(), movieURL, documentCookies))
return;
for (size_t ndx = 0; ndx < documentCookies.size(); ndx++) {
const Cookie& cookie = documentCookies[ndx];
if (cookie.name.isEmpty())
continue;
StringBuilder cookieBuilder;
addCookieParam(cookieBuilder, cookie.name, cookie.value);
addCookieParam(cookieBuilder, "path", cookie.path);
if (cookie.expires)
addCookieParam(cookieBuilder, "expires", rfc2616DateStringFromTime(cookie.expires));
if (cookie.httpOnly)
addCookieParam(cookieBuilder, "httpOnly", String());
cookieBuilder.append(";");
String cookieURL;
if (!cookie.domain.isEmpty()) {
StringBuilder urlBuilder;
urlBuilder.append(movieURL.protocol());
urlBuilder.append("://");
if (cookie.domain[0] == '.')
urlBuilder.append(cookie.domain.substring(1));
else
urlBuilder.append(cookie.domain);
if (cookie.path.length() > 1)
urlBuilder.append(cookie.path);
cookieURL = urlBuilder.toString();
} else
cookieURL = movieURL;
InternetSetCookieExW(cookieURL.charactersWithNullTermination(), 0, cookieBuilder.toString().charactersWithNullTermination(), 0, 0);
}
}
void MediaPlayerPrivateQuickTimeVisualContext::load(const String& url)
{
if (!QTMovie::initializeQuickTime()) {
m_networkState = MediaPlayer::DecodeError;
m_player->networkStateChanged();
return;
}
MediaPlayerPrivateTaskTimer::initialize();
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();
setUpCookiesForQuickTime(url);
m_movie = adoptRef(new QTMovie(m_movieClient.get()));
m_movie->load(url.characters(), url.length(), m_player->preservesPitch());
m_movie->setVolume(m_player->volume());
}
void MediaPlayerPrivateQuickTimeVisualContext::play()
{
if (!m_movie)
return;
m_startedPlaying = true;
m_movie->play();
m_visualContextTimer.startRepeating(1.0 / 30);
}
void MediaPlayerPrivateQuickTimeVisualContext::pause()
{
if (!m_movie)
return;
m_startedPlaying = false;
m_movie->pause();
m_visualContextTimer.stop();
}
float MediaPlayerPrivateQuickTimeVisualContext::duration() const
{
if (!m_movie)
return 0;
return m_movie->duration();
}
float MediaPlayerPrivateQuickTimeVisualContext::currentTime() const
{
if (!m_movie)
return 0;
return m_movie->currentTime();
}
void MediaPlayerPrivateQuickTimeVisualContext::seek(float time)
{
cancelSeek();
if (!m_movie)
return;
if (time > duration())
time = duration();
m_seekTo = time;
if (maxTimeLoaded() >= m_seekTo)
doSeek();
else
m_seekTimer.start(0, 0.5f);
}
void MediaPlayerPrivateQuickTimeVisualContext::doSeek()
{
float oldRate = m_movie->rate();
if (oldRate)
m_movie->setRate(0);
m_movie->setCurrentTime(m_seekTo);
float timeAfterSeek = currentTime();
if (oldRate && timeAfterSeek < duration())
m_movie->setRate(oldRate);
cancelSeek();
}
void MediaPlayerPrivateQuickTimeVisualContext::cancelSeek()
{
m_seekTo = -1;
m_seekTimer.stop();
}
void MediaPlayerPrivateQuickTimeVisualContext::seekTimerFired(Timer<MediaPlayerPrivateQuickTimeVisualContext>*)
{
if (!m_movie || !seeking() || currentTime() == m_seekTo) {
cancelSeek();
updateStates();
m_player->timeChanged();
return;
}
if (maxTimeLoaded() >= m_seekTo)
doSeek();
else {
MediaPlayer::NetworkState state = networkState();
if (state == MediaPlayer::Empty || state == MediaPlayer::Loaded) {
cancelSeek();
updateStates();
m_player->timeChanged();
}
}
}
bool MediaPlayerPrivateQuickTimeVisualContext::paused() const
{
if (!m_movie)
return true;
return (!m_movie->rate());
}
bool MediaPlayerPrivateQuickTimeVisualContext::seeking() const
{
if (!m_movie)
return false;
return m_seekTo >= 0;
}
IntSize MediaPlayerPrivateQuickTimeVisualContext::naturalSize() const
{
if (!m_movie)
return IntSize();
int width;
int height;
m_movie->getNaturalSize(width, height);
return IntSize(width, height);
}
bool MediaPlayerPrivateQuickTimeVisualContext::hasVideo() const
{
if (!m_movie)
return false;
return m_movie->hasVideo();
}
bool MediaPlayerPrivateQuickTimeVisualContext::hasAudio() const
{
if (!m_movie)
return false;
return m_movie->hasAudio();
}
void MediaPlayerPrivateQuickTimeVisualContext::setVolume(float volume)
{
if (!m_movie)
return;
m_movie->setVolume(volume);
}
void MediaPlayerPrivateQuickTimeVisualContext::setRate(float rate)
{
if (!m_movie)
return;
if (m_startedPlaying)
m_movie->setRate(rate);
}
void MediaPlayerPrivateQuickTimeVisualContext::setPreservesPitch(bool preservesPitch)
{
if (!m_movie)
return;
m_movie->setPreservesPitch(preservesPitch);
}
bool MediaPlayerPrivateQuickTimeVisualContext::hasClosedCaptions() const
{
if (!m_movie)
return false;
return m_movie->hasClosedCaptions();
}
void MediaPlayerPrivateQuickTimeVisualContext::setClosedCaptionsVisible(bool visible)
{
if (!m_movie)
return;
m_movie->setClosedCaptionsVisible(visible);
}
PassRefPtr<TimeRanges> MediaPlayerPrivateQuickTimeVisualContext::buffered() const
{
RefPtr<TimeRanges> timeRanges = TimeRanges::create();
float loaded = maxTimeLoaded();
if (!m_isStreaming && loaded > 0)
timeRanges->add(0, loaded);
return timeRanges.release();
}
float MediaPlayerPrivateQuickTimeVisualContext::maxTimeSeekable() const
{
return !isfinite(duration()) ? 0 : maxTimeLoaded();
}
float MediaPlayerPrivateQuickTimeVisualContext::maxTimeLoaded() const
{
if (!m_movie)
return 0;
return m_movie->maxTimeLoaded();
}
unsigned MediaPlayerPrivateQuickTimeVisualContext::bytesLoaded() const
{
if (!m_movie)
return 0;
float dur = duration();
float maxTime = maxTimeLoaded();
if (!dur)
return 0;
return totalBytes() * maxTime / dur;
}
unsigned MediaPlayerPrivateQuickTimeVisualContext::totalBytes() const
{
if (!m_movie)
return 0;
return m_movie->dataSize();
}
void MediaPlayerPrivateQuickTimeVisualContext::cancelLoad()
{
if (m_networkState < MediaPlayer::Loading || m_networkState == MediaPlayer::Loaded)
return;
tearDownVideoRendering();
m_movie.clear();
updateStates();
}
void MediaPlayerPrivateQuickTimeVisualContext::updateStates()
{
MediaPlayer::NetworkState oldNetworkState = m_networkState;
MediaPlayer::ReadyState oldReadyState = m_readyState;
long loadState = m_movie ? m_movie->loadState() : QTMovieLoadStateError;
if (loadState >= QTMovieLoadStateLoaded && m_readyState < MediaPlayer::HaveMetadata) {
m_movie->disableUnsupportedTracks(m_enabledTrackCount, m_totalTrackCount);
if (m_player->inMediaDocument()) {
if (!m_enabledTrackCount || m_enabledTrackCount != m_totalTrackCount) {
sawUnsupportedTracks();
return;
}
} else if (!m_enabledTrackCount)
loadState = QTMovieLoadStateError;
}
if (loadState >= QTMovieLoadStateComplete && !m_isStreaming) {
m_networkState = MediaPlayer::Loaded;
m_readyState = MediaPlayer::HaveEnoughData;
} else if (loadState >= QTMovieLoadStatePlaythroughOK) {
m_readyState = MediaPlayer::HaveEnoughData;
} else if (loadState >= QTMovieLoadStatePlayable) {
m_readyState = currentTime() < maxTimeLoaded() ? MediaPlayer::HaveFutureData : MediaPlayer::HaveCurrentData;
} else if (loadState >= QTMovieLoadStateLoaded) {
m_readyState = MediaPlayer::HaveMetadata;
} else if (loadState > QTMovieLoadStateError) {
m_networkState = MediaPlayer::Loading;
m_readyState = MediaPlayer::HaveNothing;
} else {
if (m_player->inMediaDocument()) {
sawUnsupportedTracks();
return;
}
float loaded = maxTimeLoaded();
if (!loaded)
m_readyState = MediaPlayer::HaveNothing;
if (!m_enabledTrackCount)
m_networkState = MediaPlayer::FormatError;
else {
if (loaded > 0)
m_networkState = MediaPlayer::DecodeError;
else
m_readyState = MediaPlayer::HaveNothing;
}
}
if (isReadyForRendering() && !hasSetUpVideoRendering())
setUpVideoRendering();
if (seeking())
m_readyState = MediaPlayer::HaveNothing;
if (m_networkState != oldNetworkState)
m_player->networkStateChanged();
if (m_readyState != oldReadyState)
m_player->readyStateChanged();
}
bool MediaPlayerPrivateQuickTimeVisualContext::isReadyForRendering() const
{
return m_readyState >= MediaPlayer::HaveMetadata && m_player->visible();
}
void MediaPlayerPrivateQuickTimeVisualContext::sawUnsupportedTracks()
{
m_movie->setDisabled(true);
m_hasUnsupportedTracks = true;
m_player->mediaPlayerClient()->mediaPlayerSawUnsupportedTracks(m_player);
}
void MediaPlayerPrivateQuickTimeVisualContext::didEnd()
{
if (m_hasUnsupportedTracks)
return;
m_startedPlaying = false;
updateStates();
m_player->timeChanged();
}
void MediaPlayerPrivateQuickTimeVisualContext::setSize(const IntSize& size)
{
if (m_hasUnsupportedTracks || !m_movie || m_size == size)
return;
m_size = size;
}
void MediaPlayerPrivateQuickTimeVisualContext::setVisible(bool visible)
{
if (m_hasUnsupportedTracks || !m_movie || m_visible == visible)
return;
m_visible = visible;
if (m_visible) {
if (isReadyForRendering())
setUpVideoRendering();
} else
tearDownVideoRendering();
}
void MediaPlayerPrivateQuickTimeVisualContext::paint(GraphicsContext* p, const IntRect& r)
{
MediaRenderingMode currentMode = currentRenderingMode();
if (currentMode == MediaRenderingNone)
return;
if (currentMode == MediaRenderingSoftwareRenderer && !m_visualContext)
return;
#if USE(ACCELERATED_COMPOSITING)
if (m_qtVideoLayer)
return;
#endif
QTPixelBuffer buffer = m_visualContext->imageForTime(0);
if (buffer.pixelBufferRef()) {
CGImageRef image = CreateCGImageFromPixelBuffer(buffer);
CGContextRef context = p->platformContext();
CGContextSaveGState(context);
CGContextTranslateCTM(context, r.x(), r.y());
CGContextTranslateCTM(context, 0, r.height());
CGContextScaleCTM(context, 1, -1);
CGContextDrawImage(context, CGRectMake(0, 0, r.width(), r.height()), image);
CGContextRestoreGState(context);
CGImageRelease(image);
}
paintCompleted(*p, r);
}
void MediaPlayerPrivateQuickTimeVisualContext::paintCompleted(GraphicsContext& context, const IntRect& rect)
{
m_newFrameAvailable = false;
}
void MediaPlayerPrivateQuickTimeVisualContext::VisualContextClient::retrieveCurrentImageProc(void* refcon)
{
static_cast<MediaPlayerPrivateQuickTimeVisualContext*>(refcon)->retrieveCurrentImage();
}
void MediaPlayerPrivateQuickTimeVisualContext::VisualContextClient::imageAvailableForTime(const QTCVTimeStamp* timeStamp)
{
callOnMainThread(&retrieveCurrentImageProc, m_parent);
}
void MediaPlayerPrivateQuickTimeVisualContext::visualContextTimerFired(Timer<MediaPlayerPrivateQuickTimeVisualContext>*)
{
if (m_visualContext && m_visualContext->isImageAvailableForTime(0))
retrieveCurrentImage();
}
static CFDictionaryRef QTCFDictionaryCreateWithDataCallback(CFAllocatorRef allocator, const UInt8* bytes, CFIndex length)
{
RetainPtr<CFDataRef> data(AdoptCF, CFDataCreateWithBytesNoCopy(allocator, bytes, length, kCFAllocatorNull));
if (!data)
return 0;
return reinterpret_cast<CFDictionaryRef>(CFPropertyListCreateFromXMLData(allocator, data.get(), kCFPropertyListImmutable, 0));
}
static CGImageRef CreateCGImageFromPixelBuffer(QTPixelBuffer buffer)
{
#if USE(ACCELERATED_COMPOSITING)
CGDataProviderRef provider = 0;
CGColorSpaceRef colorSpace = 0;
CGImageRef image = 0;
size_t bitsPerComponent = 0;
size_t bitsPerPixel = 0;
CGImageAlphaInfo alphaInfo = kCGImageAlphaNone;
if (buffer.pixelFormatIs32BGRA()) {
bitsPerComponent = 8;
bitsPerPixel = 32;
alphaInfo = (CGImageAlphaInfo)(kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little);
} else if (buffer.pixelFormatIs32ARGB()) {
bitsPerComponent = 8;
bitsPerPixel = 32;
alphaInfo = (CGImageAlphaInfo)(kCGImageAlphaNoneSkipLast | kCGBitmapByteOrder32Big);
} else {
ASSERT_NOT_REACHED();
}
CGDataProviderDirectAccessCallbacks callbacks = {
&QTPixelBuffer::dataProviderGetBytePointerCallback,
&QTPixelBuffer::dataProviderReleaseBytePointerCallback,
&QTPixelBuffer::dataProviderGetBytesAtPositionCallback,
&QTPixelBuffer::dataProviderReleaseInfoCallback,
};
colorSpace = CGColorSpaceCreateDeviceRGB();
require(colorSpace, Bail);
provider = CGDataProviderCreateDirectAccess(buffer.pixelBufferRef(), buffer.dataSize(), &callbacks);
require(provider, Bail);
QTPixelBuffer::retainCallback(buffer.pixelBufferRef());
image = CGImageCreate(buffer.width(), buffer.height(), bitsPerComponent, bitsPerPixel, buffer.bytesPerRow(), colorSpace, alphaInfo, provider, 0, false, kCGRenderingIntentDefault);
Bail:
if (provider)
CGDataProviderRelease(provider);
if (colorSpace)
CGColorSpaceRelease(colorSpace);
return image;
#else
return 0;
#endif
}
void MediaPlayerPrivateQuickTimeVisualContext::retrieveCurrentImage()
{
if (!m_visualContext)
return;
#if USE(ACCELERATED_COMPOSITING)
if (m_qtVideoLayer) {
QTPixelBuffer buffer = m_visualContext->imageForTime(0);
if (!buffer.pixelBufferRef())
return;
WKCACFLayer* layer = static_cast<WKCACFLayer*>(m_qtVideoLayer->platformLayer());
if (!buffer.lockBaseAddress()) {
if (requiredDllsAvailable()) {
if (!m_imageQueue) {
m_imageQueue = new WKCAImageQueue(buffer.width(), buffer.height(), 30);
m_imageQueue->setFlags(WKCAImageQueue::Fill, WKCAImageQueue::Fill);
layer->setContents(m_imageQueue->get());
}
RetainPtr<CFDictionaryRef> attachments(AdoptCF, QTCFDictionaryCreateCopyWithDataCallback(kCFAllocatorDefault, buffer.attachments(), &QTCFDictionaryCreateWithDataCallback));
CFTimeInterval imageTime = QTMovieVisualContext::currentHostTime();
m_imageQueue->collect();
uint64_t imageId = m_imageQueue->registerPixelBuffer(buffer.baseAddress(), buffer.dataSize(), buffer.bytesPerRow(), buffer.width(), buffer.height(), buffer.pixelFormatType(), attachments.get(), 0);
if (m_imageQueue->insertImage(imageTime, WKCAImageQueue::Buffer, imageId, WKCAImageQueue::Opaque | WKCAImageQueue::Flush, &QTPixelBuffer::imageQueueReleaseCallback, buffer.pixelBufferRef())) {
QTPixelBuffer::retainCallback(buffer.pixelBufferRef());
}
} else {
CGImageRef image = CreateCGImageFromPixelBuffer(buffer);
layer->setContents(image);
CGImageRelease(image);
}
buffer.unlockBaseAddress();
layer->rootLayer()->setNeedsRender();
}
} else
#endif
m_player->repaint();
m_visualContext->task();
}
static HashSet<String> mimeTypeCache()
{
DEFINE_STATIC_LOCAL(HashSet<String>, typeCache, ());
static bool typeListInitialized = false;
if (!typeListInitialized) {
unsigned count = QTMovie::countSupportedTypes();
for (unsigned n = 0; n < count; n++) {
const UChar* character;
unsigned len;
QTMovie::getSupportedType(n, character, len);
if (len)
typeCache.add(String(character, len));
}
typeListInitialized = true;
}
return typeCache;
}
static CFStringRef createVersionStringFromModuleName(LPCWSTR moduleName)
{
HMODULE module = GetModuleHandleW(moduleName);
if (!module)
return 0;
wchar_t filePath[MAX_PATH] = {0};
if (!GetModuleFileNameW(module, filePath, MAX_PATH))
return 0;
DWORD versionInfoSize = GetFileVersionInfoSizeW(filePath, 0);
if (!versionInfoSize)
return 0;
CFStringRef versionString = 0;
void* versionInfo = calloc(versionInfoSize, sizeof(char));
if (GetFileVersionInfo(filePath, 0, versionInfoSize, versionInfo)) {
VS_FIXEDFILEINFO* fileInfo = 0;
UINT fileInfoLength = 0;
if (VerQueryValueW(versionInfo, L"\\", reinterpret_cast<LPVOID*>(&fileInfo), &fileInfoLength)) {
versionString = CFStringCreateWithFormat(kCFAllocatorDefault, 0, CFSTR("%d.%d.%d.%d"),
HIWORD(fileInfo->dwFileVersionMS), LOWORD(fileInfo->dwFileVersionMS),
HIWORD(fileInfo->dwFileVersionLS), LOWORD(fileInfo->dwFileVersionLS));
}
}
free(versionInfo);
return versionString;
}
static bool requiredDllsAvailable()
{
static bool s_prerequisitesChecked = false;
static bool s_prerequisitesSatisfied;
static const CFStringRef kMinQuartzCoreVersion = CFSTR("1.0.42.0");
static const CFStringRef kMinCoreVideoVersion = CFSTR("1.0.1.0");
if (s_prerequisitesChecked)
return s_prerequisitesSatisfied;
s_prerequisitesChecked = true;
s_prerequisitesSatisfied = false;
CFStringRef quartzCoreString = createVersionStringFromModuleName(L"QuartzCore");
if (!quartzCoreString)
quartzCoreString = createVersionStringFromModuleName(L"QuartzCore_debug");
CFStringRef coreVideoString = createVersionStringFromModuleName(L"CoreVideo");
if (!coreVideoString)
coreVideoString = createVersionStringFromModuleName(L"CoreVideo_debug");
s_prerequisitesSatisfied = (quartzCoreString && coreVideoString
&& CFStringCompare(quartzCoreString, kMinQuartzCoreVersion, kCFCompareNumerically) != kCFCompareLessThan
&& CFStringCompare(coreVideoString, kMinCoreVideoVersion, kCFCompareNumerically) != kCFCompareLessThan);
if (quartzCoreString)
CFRelease(quartzCoreString);
if (coreVideoString)
CFRelease(coreVideoString);
return s_prerequisitesSatisfied;
}
void MediaPlayerPrivateQuickTimeVisualContext::getSupportedTypes(HashSet<String>& types)
{
types = mimeTypeCache();
}
bool MediaPlayerPrivateQuickTimeVisualContext::isAvailable()
{
return QTMovie::initializeQuickTime();
}
MediaPlayer::SupportsType MediaPlayerPrivateQuickTimeVisualContext::supportsType(const String& type, const String& codecs)
{
return mimeTypeCache().contains(type) ? (codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported) : MediaPlayer::IsNotSupported;
}
void MediaPlayerPrivateQuickTimeVisualContext::MovieClient::movieEnded(QTMovie* movie)
{
if (m_parent->m_hasUnsupportedTracks)
return;
m_parent->m_visualContextTimer.stop();
ASSERT(m_parent->m_movie.get() == movie);
m_parent->didEnd();
}
void MediaPlayerPrivateQuickTimeVisualContext::MovieClient::movieLoadStateChanged(QTMovie* movie)
{
if (m_parent->m_hasUnsupportedTracks)
return;
ASSERT(m_parent->m_movie.get() == movie);
m_parent->updateStates();
}
void MediaPlayerPrivateQuickTimeVisualContext::MovieClient::movieTimeChanged(QTMovie* movie)
{
if (m_parent->m_hasUnsupportedTracks)
return;
ASSERT(m_parent->m_movie.get() == movie);
m_parent->updateStates();
m_parent->m_player->timeChanged();
}
bool MediaPlayerPrivateQuickTimeVisualContext::hasSingleSecurityOrigin() const
{
return true;
}
MediaPlayerPrivateQuickTimeVisualContext::MediaRenderingMode MediaPlayerPrivateQuickTimeVisualContext::currentRenderingMode() const
{
if (!m_movie)
return MediaRenderingNone;
#if USE(ACCELERATED_COMPOSITING)
if (m_qtVideoLayer)
return MediaRenderingMovieLayer;
#endif
return m_visualContext ? MediaRenderingSoftwareRenderer : MediaRenderingNone;
}
MediaPlayerPrivateQuickTimeVisualContext::MediaRenderingMode MediaPlayerPrivateQuickTimeVisualContext::preferredRenderingMode() const
{
if (!m_player->frameView() || !m_movie)
return MediaRenderingNone;
#if USE(ACCELERATED_COMPOSITING)
if (supportsAcceleratedRendering() && m_player->mediaPlayerClient()->mediaPlayerRenderingCanBeAccelerated(m_player))
return MediaRenderingMovieLayer;
#endif
return MediaRenderingSoftwareRenderer;
}
void MediaPlayerPrivateQuickTimeVisualContext::setUpVideoRendering()
{
MediaRenderingMode currentMode = currentRenderingMode();
MediaRenderingMode preferredMode = preferredRenderingMode();
#if !USE(ACCELERATED_COMPOSITING)
ASSERT(preferredMode != MediaRenderingMovieLayer);
#endif
if (currentMode == preferredMode && currentMode != MediaRenderingNone)
return;
if (currentMode != MediaRenderingNone)
tearDownVideoRendering();
if (preferredMode == MediaRenderingMovieLayer)
createLayerForMovie();
#if USE(ACCELERATED_COMPOSITING)
if (currentMode == MediaRenderingMovieLayer || preferredMode == MediaRenderingMovieLayer)
m_player->mediaPlayerClient()->mediaPlayerRenderingModeChanged(m_player);
#endif
QTMovieVisualContext::Type contextType = requiredDllsAvailable() && preferredMode == MediaRenderingMovieLayer ? QTMovieVisualContext::ConfigureForCAImageQueue : QTMovieVisualContext::ConfigureForCGImage;
m_visualContext = QTMovieVisualContext::create(m_visualContextClient.get(), contextType);
m_visualContext->setMovie(m_movie.get());
}
void MediaPlayerPrivateQuickTimeVisualContext::tearDownVideoRendering()
{
#if USE(ACCELERATED_COMPOSITING)
if (m_qtVideoLayer)
destroyLayerForMovie();
#endif
m_visualContext = 0;
}
bool MediaPlayerPrivateQuickTimeVisualContext::hasSetUpVideoRendering() const
{
#if USE(ACCELERATED_COMPOSITING)
return m_qtVideoLayer || (currentRenderingMode() != MediaRenderingMovieLayer && m_visualContext);
#else
return true;
#endif
}
void MediaPlayerPrivateQuickTimeVisualContext::createLayerForMovie()
{
#if USE(ACCELERATED_COMPOSITING)
ASSERT(supportsAcceleratedRendering());
if (!m_movie || m_qtVideoLayer)
return;
m_qtVideoLayer.set(new GraphicsLayerCACF(m_layerClient.get()));
if (!m_qtVideoLayer)
return;
m_qtVideoLayer->setDrawsContent(true);
m_qtVideoLayer->setAnchorPoint(FloatPoint3D());
m_qtVideoLayer->setContentsOrientation(GraphicsLayer::CompositingCoordinatesBottomUp);
#ifndef NDEBUG
m_qtVideoLayer->setName("Video layer");
#endif
#endif
if (m_visualContext)
retrieveCurrentImage();
}
void MediaPlayerPrivateQuickTimeVisualContext::destroyLayerForMovie()
{
#if USE(ACCELERATED_COMPOSITING)
if (m_qtVideoLayer)
m_qtVideoLayer = 0;
if (m_imageQueue)
m_imageQueue = 0;
#endif
}
#if USE(ACCELERATED_COMPOSITING)
void MediaPlayerPrivateQuickTimeVisualContext::LayerClient::paintContents(const GraphicsLayer*, GraphicsContext&, GraphicsLayerPaintingPhase, const IntRect& inClip)
{
}
bool MediaPlayerPrivateQuickTimeVisualContext::supportsAcceleratedRendering() const
{
return isReadyForRendering();
}
void MediaPlayerPrivateQuickTimeVisualContext::acceleratedRenderingStateChanged()
{
setUpVideoRendering();
}
#endif
}
#endif