diff --git a/CMakeLists.txt b/CMakeLists.txt index 1dbd16e642513..e321f10f31b4a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,8 @@ cmake_minimum_required(VERSION 3.3) project(WebKit) +add_definitions(-DMETROLOGICAL=1) + set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/Source/cmake") set(ENABLE_WEBCORE ON) diff --git a/Source/WTF/wtf/Platform.h b/Source/WTF/wtf/Platform.h index d7eab3bf02fa9..49175a1f16e35 100644 --- a/Source/WTF/wtf/Platform.h +++ b/Source/WTF/wtf/Platform.h @@ -25,8 +25,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef WTF_Platform_h -#define WTF_Platform_h +#pragma once /* Include compiler specific macros */ #include @@ -288,7 +287,9 @@ || defined(__ARM_ARCH_7K__) \ || defined(__ARM_ARCH_7M__) \ || defined(__ARM_ARCH_7R__) \ - || defined(__ARM_ARCH_7S__) + || defined(__ARM_ARCH_7S__) \ + || defined(__ARM_ARCH_8__) \ + || defined(__ARM_ARCH_8A__) #define WTF_THUMB_ARCH_VERSION 4 /* RVCT sets __TARGET_ARCH_THUMB */ @@ -1295,5 +1296,3 @@ #if !OS(WINDOWS) #define HAVE_STACK_BOUNDS_FOR_NEW_THREAD 1 #endif - -#endif /* WTF_Platform_h */ diff --git a/Source/WebCore/Modules/mediasource/MediaSource.cpp b/Source/WebCore/Modules/mediasource/MediaSource.cpp index f735e4c7f9d68..5523c65ebcb34 100644 --- a/Source/WebCore/Modules/mediasource/MediaSource.cpp +++ b/Source/WebCore/Modules/mediasource/MediaSource.cpp @@ -337,7 +337,7 @@ const MediaTime& MediaSource::currentTimeFudgeFactor() const MediaTime* fudgeFactor = &MediaTime::zeroTime(); for (auto& sourceBuffer : *m_sourceBuffers) { const MediaTime* sourceBufferFudgeFactor = &sourceBuffer->currentTimeFudgeFactor(); - if (sourceBufferFudgeFactor > fudgeFactor) + if (*sourceBufferFudgeFactor > *fudgeFactor) fudgeFactor = sourceBufferFudgeFactor; } return *fudgeFactor; @@ -500,15 +500,18 @@ ExceptionOr MediaSource::setDurationInternal(const MediaTime& duration) if (newDuration == m_duration) return { }; -#if 1 +#if defined(METROLOGICAL) // Implementation to pass the YouTube MSE Conformance Tests 2016, conforming to the old MSE spec: // https://www.w3.org/TR/2016/CR-media-source-20160503/#duration-change-algorithm // 4. If the new duration is less than old duration, then call remove(new duration, old duration) // on all objects in sourceBuffers. if (m_duration.isValid() && newDuration < m_duration) { - for (auto& sourceBuffer : *m_sourceBuffers) - sourceBuffer->rangeRemoval(newDuration, m_duration); + for (auto& sourceBuffer : *m_sourceBuffers) { + unsigned length = sourceBuffer->bufferedInternal().length(); + if (length && newDuration < sourceBuffer->bufferedInternal().ranges().end(length - 1)) + sourceBuffer->rangeRemoval(newDuration, sourceBuffer->bufferedInternal().ranges().end(length - 1)); + } } #else // Upstream implementation, conforming to the latest MSE spec. @@ -855,12 +858,32 @@ bool MediaSource::isTypeSupported(const String& type) if (contentType.containerType().isEmpty()) return false; + bool ok; + unsigned channels = contentType.parameter("channels").toUInt(&ok); + if (!ok) + channels = 0; + + float width = contentType.parameter("width").toFloat(&ok); + if (!ok) + width = 0; + + float height = contentType.parameter("height").toFloat(&ok); + if (!ok) + height = 0; + + float framerate = contentType.parameter("framerate").toFloat(&ok); + if (!ok) + framerate = 0; + // 3. If type contains a media type or media subtype that the MediaSource does not support, then return false. // 4. If type contains at a codec that the MediaSource does not support, then return false. // 5. If the MediaSource does not support the specified combination of media type, media subtype, and codecs then return false. // 6. Return true. MediaEngineSupportParameters parameters; parameters.type = contentType; + parameters.channels = channels; + parameters.dimension = { width, height }; + parameters.framerate = framerate; parameters.isMediaSource = true; MediaPlayer::SupportsType supported = MediaPlayer::supportsType(parameters, 0); diff --git a/Source/WebCore/Modules/mediasource/SourceBuffer.cpp b/Source/WebCore/Modules/mediasource/SourceBuffer.cpp index 20eef8108ad43..451c1d93b9830 100644 --- a/Source/WebCore/Modules/mediasource/SourceBuffer.cpp +++ b/Source/WebCore/Modules/mediasource/SourceBuffer.cpp @@ -34,6 +34,7 @@ #if ENABLE(MEDIA_SOURCE) +#include "ActiveDOMCallbackMicrotask.h" #include "AudioTrackList.h" #include "BufferSource.h" #include "Event.h" @@ -60,6 +61,16 @@ namespace WebCore { +static inline bool mediaSourceLogEnabled() +{ +#if !LOG_DISABLED + return LOG_CHANNEL(MediaSource).state == WTFLogChannelOn; +#else + return false; +#endif +} + + static const double ExponentialMovingAverageCoefficient = 0.1; struct SourceBuffer::TrackBuffer { @@ -126,7 +137,7 @@ SourceBuffer::~SourceBuffer() MediaTime& SourceBuffer::currentTimeFudgeFactor() const { static NeverDestroyed fudgeFactorVideo(2002, 24000); - static NeverDestroyed fudgeFactorAudio(MediaTime::createWithDouble(0.03)); + static NeverDestroyed fudgeFactorAudio(300000, 10000000); // 0.03 in integer form. return (hasAudio())?fudgeFactorAudio:fudgeFactorVideo; } @@ -418,7 +429,7 @@ void SourceBuffer::seekToTime(const MediaTime& time) for (auto& trackBufferPair : m_trackBufferMap) { TrackBuffer& trackBuffer = trackBufferPair.value; - const AtomicString& trackID = trackBufferPair.key; + const auto& trackID = trackBufferPair.key; trackBuffer.needsReenqueueing = true; reenqueueMediaForTime(trackBuffer, trackID, time); @@ -540,6 +551,15 @@ ExceptionOr SourceBuffer::appendBufferInternal(const unsigned char* data, // 6. Asynchronously run the buffer append algorithm. m_appendBufferTimer.startOneShot(0_s); + // Add microtask to start append right after leaving current script context. Keep the timer active to check if append was aborted. + auto microtask = std::make_unique(MicrotaskQueue::mainThreadQueue(), *scriptExecutionContext(), [protectedThis = makeRef(*this)]() mutable { + if (protectedThis->m_appendBufferTimer.isActive()) { + protectedThis->m_appendBufferTimer.stop(); + protectedThis->appendBufferTimerFired(); + } + }); + MicrotaskQueue::mainThreadQueue().append(WTFMove(microtask)); + reportExtraMemoryAllocated(); return { }; @@ -623,7 +643,7 @@ void SourceBuffer::sourceBufferPrivateAppendComplete(AppendResult result) MediaTime currentMediaTime = m_source->currentTime(); for (auto& trackBufferPair : m_trackBufferMap) { TrackBuffer& trackBuffer = trackBufferPair.value; - const AtomicString& trackID = trackBufferPair.key; + const auto& trackID = trackBufferPair.key; if (trackBuffer.needsReenqueueing) { LOG(MediaSource, "SourceBuffer::sourceBufferPrivateAppendComplete(%p) - reenqueuing at time (%s)", this, toString(currentMediaTime).utf8().data()); @@ -671,15 +691,15 @@ static PlatformTimeRanges removeSamplesFromTrackBuffer(const DecodeOrderSampleMa MediaTime microsecond = MediaTime::createWithDouble(0.000001); #endif PlatformTimeRanges erasedRanges; - for (auto sampleIt : samples) { + for (auto& sampleIt : samples) { const DecodeOrderSampleMap::KeyType& decodeKey = sampleIt.first; #if !LOG_DISABLED size_t startBufferSize = trackBuffer.samples.sizeInBytes(); #endif - RefPtr& sample = sampleIt.second; - LOG(MediaSource, "SourceBuffer::%s(%p) - removing sample(%s)", logPrefix, buffer, toString(*sampleIt.second).utf8().data()); - + const RefPtr& sample = sampleIt.second; + if (UNLIKELY(mediaSourceLogEnabled())) + LOG(MediaSource, "SourceBuffer::%s(%p) - removing sample(%s)", logPrefix, buffer, toString(*sampleIt.second).utf8().data()); // Remove the erased samples from the TrackBuffer sample map. trackBuffer.samples.removeSample(sample.get()); @@ -759,6 +779,8 @@ void SourceBuffer::removeCodedFrames(const MediaTime& start, const MediaTime& en // NOTE: Step 3.2 will be incorrect for any random access point timestamp whose decode time is later than the sample at end, // but whose presentation time is less than the sample at end. Skip this step until step 3.3 below. + // GStreamer backend doesn't support samples division +#if !USE(GSTREAMER) // NOTE: To handle MediaSamples which may be an amalgamation of multiple shorter samples, find samples whose presentation // interval straddles the start and end times, and divide them if possible: auto divideSampleIfPossibleAtPresentationTime = [&] (const MediaTime& time) { @@ -781,6 +803,7 @@ void SourceBuffer::removeCodedFrames(const MediaTime& start, const MediaTime& en }; divideSampleIfPossibleAtPresentationTime(start); divideSampleIfPossibleAtPresentationTime(end); +#endif auto removePresentationStart = trackBuffer.samples.presentationOrder().findSampleContainingOrAfterPresentationTime(start); auto removePresentationEnd = trackBuffer.samples.presentationOrder().findSampleStartingAfterPresentationTime(end); @@ -804,7 +827,11 @@ void SourceBuffer::removeCodedFrames(const MediaTime& start, const MediaTime& en // Only force the TrackBuffer to re-enqueue if the removed ranges overlap with enqueued and possibly // not yet displayed samples. - if (trackBuffer.lastEnqueuedPresentationTime.isValid() && currentMediaTime < trackBuffer.lastEnqueuedPresentationTime) { + if (trackBuffer.lastEnqueuedPresentationTime.isValid() && currentMediaTime < trackBuffer.lastEnqueuedPresentationTime +#if defined(METROLOGICAL) + && !hasAudio() +#endif + ) { PlatformTimeRanges possiblyEnqueuedRanges(currentMediaTime, trackBuffer.lastEnqueuedPresentationTime); possiblyEnqueuedRanges.intersectWith(erasedRanges); if (possiblyEnqueuedRanges.length()) @@ -868,14 +895,21 @@ void SourceBuffer::evictCodedFrames(size_t newDataSize) // This algorithm is run to free up space in this source buffer when new data is appended. // 1. Let new data equal the data that is about to be appended to this SourceBuffer. // 2. If the buffer full flag equals false, then abort these steps. - if (!m_bufferFull) - { + if (!m_bufferFull) { LOG(MediaSource, "SourceBuffer::evictCodedFrames(%p) - buffer is not full, current buffer size %zu", this, extraMemoryCost()); return; } size_t maximumBufferSize = this->maximumBufferSize(); + // Check if app has removed enough already + if (extraMemoryCost() + newDataSize < maximumBufferSize) { + m_bufferFull = false; + return; + } + + auto& buffered = m_buffered->ranges(); + // 3. Let removal ranges equal a list of presentation time ranges that can be evicted from // the presentation to make room for the new data. @@ -885,23 +919,40 @@ void SourceBuffer::evictCodedFrames(size_t newDataSize) MediaTime currentTime = m_source->currentTime(); MediaTime maximumRangeEnd = currentTime - thirtySeconds; +#if defined(METROLOGICAL) + for (auto& trackBuffer : m_trackBufferMap.values()) { + auto prevSync = + trackBuffer.samples.decodeOrder().findSyncSamplePriorToPresentationTime(currentTime); + if (prevSync != trackBuffer.samples.decodeOrder().rend()) + maximumRangeEnd = prevSync->second->presentationTime(); + } +#endif + #if !LOG_DISABLED LOG(MediaSource, "SourceBuffer::evictCodedFrames(%p) - currentTime = %lf, require %zu bytes, maximum buffer size is %zu", this, m_source->currentTime().toDouble(), extraMemoryCost() + newDataSize, maximumBufferSize); size_t initialBufferedSize = extraMemoryCost(); #endif - MediaTime rangeStart = MediaTime::zeroTime(); + MediaTime rangeStart = buffered.start(0); MediaTime rangeEnd = rangeStart + thirtySeconds; while (rangeStart < maximumRangeEnd) { + auto removalRange = PlatformTimeRanges(rangeStart, std::min(rangeEnd, maximumRangeEnd)); + removalRange.intersectWith(buffered); + // 4. For each range in removal ranges, run the coded frame removal algorithm with start and // end equal to the removal range start and end timestamp respectively. - removeCodedFrames(rangeStart, std::min(rangeEnd, maximumRangeEnd)); - if (extraMemoryCost() + newDataSize < maximumBufferSize) { - LOG(MediaSource, "SourceBuffer::evictCodedFrames(%p) - the buffer is not full anymore.", this); - m_bufferFull = false; - break; + for (unsigned i = 0; i < removalRange.length(); ++i) { + removeCodedFrames(removalRange.start(i), removalRange.end(i)); + if (extraMemoryCost() + newDataSize < maximumBufferSize) { + LOG(MediaSource, "SourceBuffer::evictCodedFrames(%p) - the buffer is not full anymore.", this); + m_bufferFull = false; + break; + } } + if (!m_bufferFull) + break; + rangeStart += thirtySeconds; rangeEnd += thirtySeconds; } @@ -914,22 +965,38 @@ void SourceBuffer::evictCodedFrames(size_t newDataSize) // If there still isn't enough free space and there buffers in time ranges after the current range (ie. there is a gap after // the current buffered range), delete 30 seconds at a time from duration back to the current time range or 30 seconds after // currenTime whichever we hit first. - auto buffered = m_buffered->ranges(); size_t currentTimeRange = buffered.find(currentTime); + +#if defined(METROLOGICAL) + MediaTime minimumRangeStart = currentTime + thirtySeconds; + + if (currentTimeRange == buffered.length() - 1) { + LOG(MediaSource, "SourceBuffer::evictCodedFrames(%p) - evicted %zu bytes from the beginning but failed to free enough.", this, initialBufferedSize - extraMemoryCost()); + minimumRangeStart = m_groupEndTimestamp; + } + + if (m_source->duration().isPositiveInfinite()) { + if (!buffered.length()) + return; + rangeEnd = buffered.end(buffered.length() - 1); + } else + rangeEnd = m_source->duration(); +#else if (currentTimeRange == buffered.length() - 1) { LOG(MediaSource, "SourceBuffer::evictCodedFrames(%p) - evicted %zu bytes but FAILED to free enough", this, initialBufferedSize - extraMemoryCost()); return; } MediaTime minimumRangeStart = currentTime + thirtySeconds; - LOG(MediaSource, "SourceBuffer::evictCodedFrames(%p) - minimumRangeStart: %f, duration: %f", this, minimumRangeStart.toDouble(), m_source->duration().toDouble()); rangeEnd = m_source->duration(); +#endif rangeStart = rangeEnd - thirtySeconds; + LOG(MediaSource, "SourceBuffer::evictCodedFrames(%p) - minimumRangeStart: %f, duration: %f", this, minimumRangeStart.toDouble(), m_source->duration().toDouble()); + auto removeFramesWhileFull = [&] (PlatformTimeRanges& ranges) { - for (int i = ranges.length()-1; i >= 0; --i) - { + for (int i = ranges.length()-1; i >= 0; --i) { LOG(MediaSource, "SourceBuffer::evictCodedFrames(%p) - removing coded frames in range [%f, %f)", this, ranges.start(i).toDouble(), ranges.end(i).toDouble()); removeCodedFrames(ranges.start(i), ranges.end(i)); if (extraMemoryCost() + newDataSize < maximumBufferSize) { @@ -952,15 +1019,14 @@ void SourceBuffer::evictCodedFrames(size_t newDataSize) removeFramesWhileFull(intersectedRanges); - if (m_bufferFull == false) + if (!m_bufferFull) break; rangeStart -= thirtySeconds; rangeEnd -= thirtySeconds; } - if (m_bufferFull && currentTimeRange == notFound) - { + if (m_bufferFull && currentTimeRange == notFound) { LOG(MediaSource, "SourceBuffer::evictCodedFrames(%p) - We tried hard to evict, but the buffer is still full and current time is unbuffered, let's try to remove more buffered data.", this); removeFramesWhileFull(buffered); } @@ -1012,11 +1078,11 @@ static void maximumBufferSizeDefaults(size_t& maxBufferSizeVideo, size_t& maxBuf } } - if (maxBufferSizeAudio == 0) + if (!maxBufferSizeAudio) maxBufferSizeAudio = 3 * 1024 * 1024; - if (maxBufferSizeVideo == 0) + if (!maxBufferSizeVideo) maxBufferSizeVideo = 30 * 1024 * 1024; - if (maxBufferSizeText == 0) + if (!maxBufferSizeText) maxBufferSizeText = 1 * 1024 * 1024; } @@ -1637,7 +1703,7 @@ void SourceBuffer::sourceBufferPrivateDidReceiveSample(MediaSample& sample) if (range.first != trackBuffer.samples.presentationOrder().end()) erasedSamples.addRange(range.first, range.second); - } while(false); + } while (false); } // 1.16 Remove decoding dependencies of the coded frames removed in the previous step: @@ -1658,7 +1724,11 @@ void SourceBuffer::sourceBufferPrivateDidReceiveSample(MediaSample& sample) // Only force the TrackBuffer to re-enqueue if the removed ranges overlap with enqueued and possibly // not yet displayed samples. MediaTime currentMediaTime = m_source->currentTime(); - if (currentMediaTime < trackBuffer.lastEnqueuedPresentationTime) { + if (currentMediaTime < trackBuffer.lastEnqueuedPresentationTime +#if defined(METROLOGICAL) + && !hasAudio() +#endif + ) { PlatformTimeRanges possiblyEnqueuedRanges(currentMediaTime, trackBuffer.lastEnqueuedPresentationTime); possiblyEnqueuedRanges.intersectWith(erasedRanges); if (possiblyEnqueuedRanges.length()) @@ -1730,6 +1800,19 @@ void SourceBuffer::sourceBufferPrivateDidReceiveSample(MediaSample& sample) // duration set to the maximum of the current duration and the group end timestamp. if (m_groupEndTimestamp > m_source->duration()) m_source->setDurationInternal(m_groupEndTimestamp); + + // To avoid playback pipeline starvation start providing media data as soon as we can + const auto& trackID = sample.trackID(); + auto it = m_trackBufferMap.find(trackID); + if (it != m_trackBufferMap.end() && m_private->isReadyForMoreSamples(trackID)) { + TrackBuffer& trackBuffer = it->value; + if (!trackBuffer.needsReenqueueing + && trackBuffer.lastEnqueuedDecodeEndTime.isValid() + && trackBuffer.lastDecodeTimestamp.isValid() + && abs(trackBuffer.lastEnqueuedDecodeEndTime - trackBuffer.lastDecodeTimestamp) > MediaTime::createWithDouble(0.350)) { + provideMediaData(trackBuffer, trackID); + } + } } bool SourceBuffer::hasAudio() const diff --git a/Source/WebCore/html/HTMLMediaElement.cpp b/Source/WebCore/html/HTMLMediaElement.cpp index 9bae4640bd153..4aa4afd145fc5 100644 --- a/Source/WebCore/html/HTMLMediaElement.cpp +++ b/Source/WebCore/html/HTMLMediaElement.cpp @@ -4608,7 +4608,7 @@ void HTMLMediaElement::mediaPlayerTimeChanged(MediaPlayer*) invalidateCachedTime(); bool wasSeeking = seeking(); - // 4.8.10.9 step 14 & 15. Needed if no ReadyState change is associated with the seek. + // 4.8.10.9 step 14 & 15. Needed if no ReadyState change is associated with the seek. if (m_seekRequested && m_readyState >= HAVE_CURRENT_DATA && !m_player->seeking()) finishSeek(); diff --git a/Source/WebCore/page/animation/AnimationBase.cpp b/Source/WebCore/page/animation/AnimationBase.cpp index fc463c21877c3..f0c2786929ba0 100644 --- a/Source/WebCore/page/animation/AnimationBase.cpp +++ b/Source/WebCore/page/animation/AnimationBase.cpp @@ -658,6 +658,9 @@ double AnimationBase::progress(double scale, double offset, const TimingFunction if (fillingForwards()) elapsedTime = duration; + if (elapsedTime > duration && std::abs(m_animation->iterationCount() - 1.0) < 0.0001) + elapsedTime = duration; + double fractionalTime = this->fractionalTime(scale, elapsedTime, offset); if (m_animation->iterationCount() > 0 && elapsedTime >= duration) { diff --git a/Source/WebCore/platform/graphics/MediaPlayer.h b/Source/WebCore/platform/graphics/MediaPlayer.h index 6c2b8634e7595..bea2018e87dd5 100644 --- a/Source/WebCore/platform/graphics/MediaPlayer.h +++ b/Source/WebCore/platform/graphics/MediaPlayer.h @@ -23,9 +23,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef MediaPlayer_h -#define MediaPlayer_h - +#pragma once #if ENABLE(VIDEO) #include "GraphicsTypes3D.h" @@ -121,6 +119,9 @@ struct MediaEngineSupportParameters { ContentType type; URL url; String keySystem; + unsigned int channels; + FloatSize dimension; + float framerate; bool isMediaSource { false }; bool isMediaStream { false }; Vector contentTypesRequiringHardwareSupport; @@ -368,7 +369,7 @@ class MediaPlayer : public MediaPlayerEnums, public RefCounted { bool inMediaDocument() const; IntSize size() const { return m_size; } - void setSize(const IntSize& size); + void setSize(const IntSize&); void setPosition(const IntPoint&); bool load(const URL&, const ContentType&, const String& keySystem); @@ -445,7 +446,7 @@ class MediaPlayer : public MediaPlayerEnums, public RefCounted { void setMuted(bool); bool hasClosedCaptions() const; - void setClosedCaptionsVisible(bool closedCaptionsVisible); + void setClosedCaptionsVisible(bool); void paint(GraphicsContext&, const FloatRect&); void paintCurrentFrameInContext(GraphicsContext&, const FloatRect&); @@ -711,5 +712,3 @@ struct LogArgument { } #endif // ENABLE(VIDEO) - -#endif diff --git a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp index 0e2bb76252a27..5e300e14db064 100644 --- a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp @@ -310,17 +310,17 @@ void MediaPlayerPrivateGStreamer::commitLoad() // utility function for bcm nexus seek functionality static GstElement* findVideoDecoder(GstElement *element) { - GstElement *re = NULL; + GstElement* re = nullptr; if (GST_IS_BIN(element)) { GstIterator* it = gst_bin_iterate_elements(GST_BIN(element)); GValue item = G_VALUE_INIT; bool done = false; - while(!done) { + while (!done) { switch (gst_iterator_next(it, &item)) { case GST_ITERATOR_OK: { GstElement *next = GST_ELEMENT(g_value_get_object(&item)); - done = (re = findVideoDecoder(next)) != NULL; + done = (re = findVideoDecoder(next)); g_value_reset (&item); break; } @@ -483,6 +483,9 @@ void MediaPlayerPrivateGStreamer::play() m_isEndReached = false; m_delayingLoad = false; m_preload = MediaPlayer::Auto; + // Make sure we properly detect live stream on play. + if (!isMediaSource()) + totalBytes(); setDownloadBuffering(); GST_DEBUG("Play"); } else { @@ -1430,7 +1433,7 @@ float MediaPlayerPrivateGStreamer::maxTimeLoaded() const return 0.0f; float loaded = m_maxTimeLoaded; - if (!loaded && !m_fillTimer.isActive()){ + if (!loaded && !m_fillTimer.isActive()) { if (m_cachedPosition > 0) loaded = m_cachedPosition; else if (m_durationAtEOS) @@ -2153,7 +2156,7 @@ void MediaPlayerPrivateGStreamer::setDownloadBuffering() unsigned flagDownload = getGstPlayFlag("download"); // We don't want to stop downloading if we already started it. - if (flags & flagDownload && m_readyState > MediaPlayer::HaveNothing && !m_resetPipeline) + if (flags & flagDownload && m_readyState > MediaPlayer::HaveNothing && !m_resetPipeline && !isLiveStream()) return; bool shouldDownload = !isLiveStream() && m_preload == MediaPlayer::Auto && !isMediaDiskCacheDisabled(); diff --git a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerBase.cpp b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerBase.cpp index 3b3c8c4d449ca..83fb8c14a7ebf 100644 --- a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerBase.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerBase.cpp @@ -134,6 +134,13 @@ using namespace std; namespace WebCore { +#if ENABLE(NATIVE_AUDIO) +static const GstStreamVolumeFormat volumeFormat = GST_STREAM_VOLUME_FORMAT_LINEAR; +#else +static const GstStreamVolumeFormat volumeFormat = GST_STREAM_VOLUME_FORMAT_CUBIC; +#endif + + void registerWebKitGStreamerElements() { if (!webkitGstCheckVersion(1, 6, 1)) @@ -260,8 +267,8 @@ MediaPlayerPrivateGStreamerBase::MediaPlayerPrivateGStreamerBase(MediaPlayer* pl , m_readyState(MediaPlayer::HaveNothing) , m_networkState(MediaPlayer::Empty) , m_isEndReached(false) - , m_drawTimer(RunLoop::main(), this, &MediaPlayerPrivateGStreamerBase::repaint) , m_usingFallbackVideoSink(false) + , m_drawTimer(RunLoop::main(), this, &MediaPlayerPrivateGStreamerBase::repaint) { g_mutex_init(&m_sampleMutex); #if USE(COORDINATED_GRAPHICS_THREADED) @@ -555,7 +562,7 @@ bool MediaPlayerPrivateGStreamerBase::ensureGstGLContext() FloatSize MediaPlayerPrivateGStreamerBase::naturalSize() const { if (!hasVideo()) - return FloatSize(); + return { }; if (!m_videoSize.isEmpty()) return m_videoSize; @@ -565,7 +572,7 @@ FloatSize MediaPlayerPrivateGStreamerBase::naturalSize() const GRefPtr caps; // We may not have enough data available for the video sink yet. if (!GST_IS_SAMPLE(m_sample.get())) - return FloatSize(); + return { }; if (GST_IS_SAMPLE(m_sample.get()) && !caps) caps = gst_sample_get_caps(m_sample.get()); @@ -577,7 +584,7 @@ FloatSize MediaPlayerPrivateGStreamerBase::naturalSize() const } if (!caps) - return FloatSize(); + return { }; // TODO: handle possible clean aperture data. See // https://bugzilla.gnome.org/show_bug.cgi?id=596571 @@ -590,7 +597,13 @@ FloatSize MediaPlayerPrivateGStreamerBase::naturalSize() const IntSize originalSize; GstVideoFormat format; if (!getVideoSizeAndFormatFromCaps(caps.get(), originalSize, format, pixelAspectRatioNumerator, pixelAspectRatioDenominator, stride)) - return FloatSize(); + return { }; + + // Sanity check for the unlikely, but reproducible case when getVideoSizeAndFormatFromCaps returns incorrect values. + if (!originalSize.width() || !originalSize.height() || !pixelAspectRatioNumerator || !pixelAspectRatioNumerator) { + GST_DEBUG("getVideoSizeAndFormatFromCaps returned an invalid info, returning an empty size"); + return { }; + } #if USE(TEXTURE_MAPPER_GL) // When using accelerated compositing, if the video is tagged as rotated 90 or 270 degrees, swap width and height. @@ -639,7 +652,7 @@ void MediaPlayerPrivateGStreamerBase::setVolume(float volume) return; GST_DEBUG("Setting volume: %f", volume); - gst_stream_volume_set_volume(m_volumeElement.get(), GST_STREAM_VOLUME_FORMAT_CUBIC, static_cast(volume)); + gst_stream_volume_set_volume(m_volumeElement.get(), volumeFormat, static_cast(volume)); } float MediaPlayerPrivateGStreamerBase::volume() const @@ -647,7 +660,7 @@ float MediaPlayerPrivateGStreamerBase::volume() const if (!m_volumeElement) return 0; - return gst_stream_volume_get_volume(m_volumeElement.get(), GST_STREAM_VOLUME_FORMAT_CUBIC); + return gst_stream_volume_get_volume(m_volumeElement.get(), volumeFormat); } @@ -656,7 +669,7 @@ void MediaPlayerPrivateGStreamerBase::notifyPlayerOfVolumeChange() if (!m_player || !m_volumeElement) return; double volume; - volume = gst_stream_volume_get_volume(m_volumeElement.get(), GST_STREAM_VOLUME_FORMAT_CUBIC); + volume = gst_stream_volume_get_volume(m_volumeElement.get(), volumeFormat); // get_volume() can return values superior to 1.0 if the user // applies software user gain via third party application (GNOME // volume control for instance). @@ -666,8 +679,10 @@ void MediaPlayerPrivateGStreamerBase::notifyPlayerOfVolumeChange() void MediaPlayerPrivateGStreamerBase::volumeChangedCallback(MediaPlayerPrivateGStreamerBase* player) { +#if PLATFORM(WPE) // This is called when m_volumeElement receives the notify::volume signal. GST_DEBUG("Volume changed to: %f", player->volume()); +#endif player->m_notifier->notify(MainThreadNotification::VolumeChanged, [player] { player->notifyPlayerOfVolumeChange(); }); } @@ -910,7 +925,12 @@ GstFlowReturn MediaPlayerPrivateGStreamerBase::newPrerollCallback(GstElement* si void MediaPlayerPrivateGStreamerBase::clearCurrentBuffer() { WTF::GMutexLocker lock(m_sampleMutex); - m_sample.clear(); + + // Replace by a new sample having only the caps, so this dummy sample is still useful to get the dimensions. + // This prevents resizing problems when the video changes its quality and a DRAIN is performed. + const GstStructure* info = gst_sample_get_info(m_sample.get()); + m_sample = adoptGRef(gst_sample_new(nullptr, gst_sample_get_caps(m_sample.get()), + gst_sample_get_segment(m_sample.get()), info ? gst_structure_copy(info) : nullptr)); { LockHolder locker(m_platformLayerProxy->lock()); @@ -954,7 +974,7 @@ void MediaPlayerPrivateGStreamerBase::updateVideoRectangle() GRefPtr sinkElement; g_object_get(m_pipeline.get(), "video-sink", &sinkElement.outPtr(), nullptr); - if(!sinkElement) + if (!sinkElement) return; GST_INFO("Setting video sink size and position to x:%d y:%d, width=%d, height=%d", m_position.x(), m_position.y(), m_size.width(), m_size.height()); @@ -1181,17 +1201,18 @@ GstElement* MediaPlayerPrivateGStreamerBase::createVideoSinkGL() gst_element_add_pad(videoSink, gst_ghost_pad_new("sink", pad.get())); pad = adoptGRef(gst_element_get_static_pad(appsink, "sink")); - gst_pad_add_probe (pad.get(), GST_PAD_PROBE_TYPE_EVENT_FLUSH, [] (GstPad*, GstPadProbeInfo* info, gpointer userData) -> GstPadProbeReturn { - if (GST_EVENT_TYPE (GST_PAD_PROBE_INFO_EVENT (info)) != GST_EVENT_FLUSH_START) - return GST_PAD_PROBE_OK; + gst_pad_add_probe (pad.get(), GST_PAD_PROBE_TYPE_EVENT_FLUSH, + [] (GstPad*, GstPadProbeInfo* info, gpointer userData) -> GstPadProbeReturn { + if (GST_EVENT_TYPE (GST_PAD_PROBE_INFO_EVENT (info)) != GST_EVENT_FLUSH_START) + return GST_PAD_PROBE_OK; - auto* player = static_cast(userData); - player->clearCurrentBuffer(); - return GST_PAD_PROBE_OK; - }, this, nullptr); - - g_object_set_data(G_OBJECT(appsink), "player", (gpointer) this); - gst_pad_set_query_function(pad.get(), appSinkSinkQuery); + auto* player = static_cast(userData); + player->clearCurrentBuffer(); + return GST_PAD_PROBE_OK; + }, this, nullptr); + + g_object_set_data(G_OBJECT(appsink), "player", (gpointer) this); + gst_pad_set_query_function(pad.get(), appSinkSinkQuery); if (!result) { GST_WARNING("Failed to link GstGL elements"); @@ -1257,7 +1278,7 @@ void MediaPlayerPrivateGStreamerBase::setStreamVolumeElement(GstStreamVolume* vo // https://bugs.webkit.org/show_bug.cgi?id=118974 for more information. if (!m_player->platformVolumeConfigurationRequired()) { GST_DEBUG("Setting stream volume to %f", m_player->volume()); - g_object_set(m_volumeElement.get(), "volume", m_player->volume(), nullptr); + setVolume(m_player->volume()); } else GST_DEBUG("Not setting stream volume, trusting system one"); diff --git a/Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp index 257875f423285..9b9ee57a4ea5e 100644 --- a/Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp @@ -622,7 +622,7 @@ static gboolean webKitWebSrcQueryWithParent(GstPad* pad, GstObject* parent, GstQ WTF::GMutexLocker gstLocker(*GST_OBJECT_GET_LOCK(src)); GstContext* context = gst_context_new("http-headers", FALSE); - gst_context_make_writable(context); + context = gst_context_make_writable(context); GstStructure* contextStructure = gst_context_writable_structure(context); const gchar* cookiesArray[] = { src->priv->cookies.get(), nullptr}; @@ -984,4 +984,3 @@ void CachedResourceStreamingClient::loadFinished(PlatformMediaResource&) } #endif // USE(GSTREAMER) - diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/AppendPipeline.cpp b/Source/WebCore/platform/graphics/gstreamer/mse/AppendPipeline.cpp index 9efce866d7fd0..e1ba58a7d97c9 100644 --- a/Source/WebCore/platform/graphics/gstreamer/mse/AppendPipeline.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/mse/AppendPipeline.cpp @@ -172,6 +172,7 @@ AppendPipeline::~AppendPipeline() { ASSERT(WTF::isMainThread()); + GST_TRACE("Destroying AppendPipeline (%p)", this); { LockHolder locker(m_newSampleLock); setAppendState(AppendState::Invalid); @@ -184,8 +185,6 @@ AppendPipeline::~AppendPipeline() m_padAddRemoveCondition.notifyOne(); } - GST_TRACE("Destroying AppendPipeline (%p)", this); - // FIXME: Maybe notify appendComplete here? if (m_pipeline) { @@ -261,9 +260,23 @@ void AppendPipeline::handleNeedContextSyncMessage(GstMessage* message) const gchar* contextType = nullptr; gst_message_parse_context_type(message, &contextType); GST_TRACE("context type: %s", contextType); - if (!g_strcmp0(contextType, "drm-preferred-decryption-system-id") - && m_appendState != AppendPipeline::AppendState::KeyNegotiation) - setAppendState(AppendPipeline::AppendState::KeyNegotiation); + + LockHolder locker(m_appendStateTransitionLock); + if (m_appendState == AppendState::Invalid) + return; + + if (!g_strcmp0(contextType, "drm-preferred-decryption-system-id")) { + if (WTF::isMainThread()) + transitionTo(AppendState::KeyNegotiation); + else { + GstStructure* structure = gst_structure_new("transition-main-thread", "transition", G_TYPE_INT, AppendState::KeyNegotiation, nullptr); + GstMessage* message = gst_message_new_application(GST_OBJECT(m_demux.get()), structure); + if (gst_bus_post(m_bus.get(), message)) { + GST_TRACE("transition-main-thread KeyNegotiation sent to the bus"); + m_appendStateTransitionCondition.wait(m_appendStateTransitionLock); + } + } + } // MediaPlayerPrivateGStreamerBase will take care of setting up encryption. if (m_playerPrivate) @@ -294,6 +307,14 @@ void AppendPipeline::handleApplicationMessage(GstMessage* message) return; } + if (gst_structure_has_name(structure, "transition-main-thread")) { + GST_TRACE("Received transition-main-thread in main thread"); + AppendState nextState; + gst_structure_get(structure, "transition", G_TYPE_INT, &nextState, nullptr); + transitionTo(nextState); + return; + } + if (gst_structure_has_name(structure, "appsink-caps-changed")) { appsinkCapsChanged(); return; @@ -707,7 +728,7 @@ void AppendPipeline::appsinkNewSample(GstSample* sample) // If we're beyond the duration, ignore this sample and the remaining ones. MediaTime duration = m_mediaSourceClient->duration(); - if (duration.isValid() && !duration.indefiniteTime() && mediaSample->presentationTime() > duration) { + if (duration.isValid() && !duration.isIndefinite() && mediaSample->presentationTime() > duration) { GST_DEBUG("Detected sample (%f) beyond the duration (%f), declaring LastSample", mediaSample->presentationTime().toFloat(), duration.toFloat()); setAppendState(AppendState::LastSample); m_flowReturn = GST_FLOW_OK; @@ -724,6 +745,7 @@ void AppendPipeline::appsinkNewSample(GstSample* sample) } m_sourceBufferPrivate->didReceiveSample(*mediaSample); + setAppendState(AppendState::Sampling); m_flowReturn = GST_FLOW_OK; m_newSampleCondition.notifyOne(); @@ -870,6 +892,7 @@ GstFlowReturn AppendPipeline::pushNewBuffer(GstBuffer* buffer) } else { setAppendState(AppendPipeline::AppendState::Ongoing); GST_TRACE("pushing new buffer %p", buffer); + result = gst_app_src_push_buffer(GST_APP_SRC(appsrc()), buffer); } @@ -999,6 +1022,22 @@ void AppendPipeline::connectDemuxerSrcPadToAppsinkFromAnyThread(GstPad* demuxerS } } +void AppendPipeline::transitionTo(AppendState nextState) +{ + ASSERT(WTF::isMainThread()); + + LockHolder locker(m_appendStateTransitionLock); + + if (m_appendState == AppendState::Invalid || m_appendState == nextState || !m_playerPrivate) { + m_appendStateTransitionCondition.notifyOne(); + return; + } + + setAppendState(nextState); + + m_appendStateTransitionCondition.notifyOne(); +} + void AppendPipeline::connectDemuxerSrcPadToAppsink(GstPad* demuxerSrcPad) { ASSERT(WTF::isMainThread()); diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/AppendPipeline.h b/Source/WebCore/platform/graphics/gstreamer/mse/AppendPipeline.h index d9d9105d89a23..d2f363c7e7528 100644 --- a/Source/WebCore/platform/graphics/gstreamer/mse/AppendPipeline.h +++ b/Source/WebCore/platform/graphics/gstreamer/mse/AppendPipeline.h @@ -88,6 +88,8 @@ class AppendPipeline : public ThreadSafeRefCounted { void connectDemuxerSrcPadToAppsinkFromAnyThread(GstPad*); void connectDemuxerSrcPadToAppsink(GstPad*); + void transitionTo(AppendState); + void reportAppsrcAtLeastABufferLeft(); void reportAppsrcNeedDataReceived(); @@ -127,6 +129,8 @@ class AppendPipeline : public ThreadSafeRefCounted { Condition m_newSampleCondition; Lock m_padAddRemoveLock; Condition m_padAddRemoveCondition; + Lock m_appendStateTransitionLock; + Condition m_appendStateTransitionCondition; GRefPtr m_appsinkCaps; GRefPtr m_demuxerSrcPadCaps; diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/GStreamerMediaSample.h b/Source/WebCore/platform/graphics/gstreamer/mse/GStreamerMediaSample.h index 49e12b5c3f6e0..f8980e4321a5f 100644 --- a/Source/WebCore/platform/graphics/gstreamer/mse/GStreamerMediaSample.h +++ b/Source/WebCore/platform/graphics/gstreamer/mse/GStreamerMediaSample.h @@ -26,6 +26,7 @@ #include "GRefPtrGStreamer.h" #include "MediaSample.h" #include +#include #include namespace WebCore { @@ -55,7 +56,7 @@ class GStreamerMediaSample : public MediaSample { Ref createNonDisplayingCopy() const override; SampleFlags flags() const override { return m_flags; } PlatformSample platformSample() override { return PlatformSample(); } - void dump(PrintStream&) const override { } + void dump(PrintStream& out) const override { out.print("{PTS(", presentationTime(), "), DTS(", decodeTime(), "), duration(", duration(), ")}"); } private: GStreamerMediaSample(GstSample*, const FloatSize& presentationSize, const AtomicString& trackId); diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/MediaPlayerPrivateGStreamerMSE.cpp b/Source/WebCore/platform/graphics/gstreamer/mse/MediaPlayerPrivateGStreamerMSE.cpp index e549af4f2f5dd..d0928c8ac03d9 100644 --- a/Source/WebCore/platform/graphics/gstreamer/mse/MediaPlayerPrivateGStreamerMSE.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/mse/MediaPlayerPrivateGStreamerMSE.cpp @@ -318,19 +318,26 @@ bool MediaPlayerPrivateGStreamerMSE::doSeek() } // Check if MSE has samples for requested time and defer actual seek if needed. - if (!isTimeBuffered(seekTime)) { - GST_DEBUG("[Seek] Delaying the seek: MSE is not ready"); - GstStateChangeReturn setStateResult = gst_element_set_state(m_pipeline.get(), GST_STATE_PAUSED); - if (setStateResult == GST_STATE_CHANGE_FAILURE) { - GST_DEBUG("[Seek] Cannot seek, failed to pause playback pipeline."); - webKitMediaSrcSetReadyForSamples(WEBKIT_MEDIA_SRC(m_source.get()), true); - m_seeking = false; - return false; + // This condition on m_readyState must match the conditions which trigger completeSeek() in + // MediaSource::monitorSourceBuffers(). + if (!isTimeBuffered(seekTime) || m_readyState < MediaPlayer::HaveCurrentData) { + // Media source may trigger seek completion even when the target time is not yet buffered, + // in this case it is better continue the seek and wait for the app to provide media data. + m_mseSeekCompleted = true; + m_mediaSource->seekToTime(seekTime); + if (!m_mseSeekCompleted) { + GST_DEBUG("[Seek] Delaying the seek: MSE is not ready"); + m_readyState = MediaPlayer::HaveMetadata; + GstStateChangeReturn setStateResult = gst_element_set_state(m_pipeline.get(), GST_STATE_PAUSED); + if (setStateResult == GST_STATE_CHANGE_FAILURE) { + GST_WARNING("[Seek] Cannot seek, failed to pause playback pipeline."); + webKitMediaSrcSetReadyForSamples(WEBKIT_MEDIA_SRC(m_source.get()), true); + m_seeking = false; + return false; + } + return true; } - m_readyState = MediaPlayer::HaveMetadata; - notifySeekNeedsDataForTime(seekTime); - ASSERT(!m_mseSeekCompleted); - return true; + GST_DEBUG("[Seek] The target seek time is not buffered yet, but media source says OK to continue the seek, seekTime=%f", seekTime.toDouble()); } // Complete previous MSE seek if needed. @@ -404,6 +411,7 @@ void MediaPlayerPrivateGStreamerMSE::maybeFinishSeek() // Right now we can use m_seekTime as a fallback. m_canFallBackToLastFinishedSeekPosition = true; timeChanged(); + m_player->readyStateChanged(); } void MediaPlayerPrivateGStreamerMSE::updatePlaybackRate() @@ -442,7 +450,7 @@ void MediaPlayerPrivateGStreamerMSE::setReadyState(MediaPlayer::ReadyState ready GstStateChangeReturn getStateResult = gst_element_get_state(m_pipeline.get(), &pipelineState, nullptr, 250 * GST_NSECOND); bool isPlaying = (getStateResult == GST_STATE_CHANGE_SUCCESS && pipelineState == GST_STATE_PLAYING); - if (m_readyState == MediaPlayer::HaveMetadata && oldReadyState > MediaPlayer::HaveMetadata && isPlaying) { + if (m_readyState == MediaPlayer::HaveMetadata && oldReadyState > MediaPlayer::HaveMetadata && isPlaying && !playbackPipelineHasFutureData()) { GST_TRACE("Changing pipeline to PAUSED..."); bool ok = changePipelineState(GST_STATE_PAUSED); GST_TRACE("Changed pipeline to PAUSED: %s", ok ? "Success" : "Error"); @@ -577,7 +585,7 @@ void MediaPlayerPrivateGStreamerMSE::updateStates() } #if PLATFORM(BROADCOM) // this code path needs a proper review in case it can be generalized to all platforms. - bool buffering = !isTimeBuffered(currentMediaTime()); + bool buffering = !isTimeBuffered(currentMediaTime()) && !playbackPipelineHasFutureData(); #else bool buffering = m_buffering; #endif @@ -681,6 +689,15 @@ bool MediaPlayerPrivateGStreamerMSE::isTimeBuffered(const MediaTime &time) const return result; } +bool MediaPlayerPrivateGStreamerMSE::playbackPipelineHasFutureData() const +{ + if (!m_playbackPipeline || m_isEndReached || m_errorOccured) + return false; + + MediaTime position = MediaPlayerPrivateGStreamer::currentMediaTime(); + return m_playbackPipeline->hasFutureData(position); +} + void MediaPlayerPrivateGStreamerMSE::setMediaSourceClient(Ref client) { m_mediaSourceClient = client.ptr(); @@ -863,6 +880,17 @@ bool MediaPlayerPrivateGStreamerMSE::supportsCodecs(const String& codecs) return true; } +FloatSize MediaPlayerPrivateGStreamerMSE::naturalSize() const +{ + if (!hasVideo()) + return FloatSize(); + + if (!m_videoSize.isEmpty()) + return m_videoSize; + + return MediaPlayerPrivateGStreamerBase::naturalSize(); +} + MediaPlayer::SupportsType MediaPlayerPrivateGStreamerMSE::supportsType(const MediaEngineSupportParameters& parameters) { MediaPlayer::SupportsType result = MediaPlayer::IsNotSupported; @@ -880,6 +908,18 @@ MediaPlayer::SupportsType MediaPlayerPrivateGStreamerMSE::supportsType(const Med return result; } + // We shouldn't accept media that the player can't actually play. Using AAC audio, 8K and 60 fps limits here. + // AAC supports up to 96 channels. + if (parameters.channels > 96) + return result; + + // 8K is up to 7680*4320 + if (parameters.dimension.width() > 7680.0 || parameters.dimension.height() > 4320.0) + return result; + + if (parameters.framerate > 60.0) + return result; + // Spec says we should not return "probably" if the codecs string is empty. if (mimeTypeCache().contains(containerType)) { String codecs = parameters.type.parameter(ContentType::codecsParameter()); @@ -902,11 +942,19 @@ void MediaPlayerPrivateGStreamerMSE::markEndOfStream(MediaSourcePrivate::EndOfSt updateStates(); } +void MediaPlayerPrivateGStreamerMSE::unmarkEndOfStream() +{ + GST_DEBUG("Unmarking end of stream"); + m_eosPending = false; +} + MediaTime MediaPlayerPrivateGStreamerMSE::currentMediaTime() const { + MediaTime cachedPosition = MediaTime::createWithFloat(m_cachedPosition); MediaTime position = MediaPlayerPrivateGStreamer::currentMediaTime(); + MediaTime playbackProgress = abs(position - cachedPosition); - if (m_eosPending && (paused() || (position >= durationMediaTime()))) { + if (m_eosPending && abs(position - durationMediaTime()) < MediaTime(GST_SECOND, GST_SECOND) && !playbackProgress) { if (m_networkState != MediaPlayer::Loaded) { m_networkState = MediaPlayer::Loaded; m_player->networkStateChanged(); @@ -914,7 +962,8 @@ MediaTime MediaPlayerPrivateGStreamerMSE::currentMediaTime() const m_eosPending = false; m_isEndReached = true; - m_cachedPosition = m_mediaTimeDuration.toFloat(); + position = m_mediaTimeDuration; + m_cachedPosition = position.toFloat(); m_durationAtEOS = m_mediaTimeDuration.toFloat(); m_player->timeChanged(); } diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/MediaPlayerPrivateGStreamerMSE.h b/Source/WebCore/platform/graphics/gstreamer/mse/MediaPlayerPrivateGStreamerMSE.h index e67c32075ee7c..0392b1fe3eee1 100644 --- a/Source/WebCore/platform/graphics/gstreamer/mse/MediaPlayerPrivateGStreamerMSE.h +++ b/Source/WebCore/platform/graphics/gstreamer/mse/MediaPlayerPrivateGStreamerMSE.h @@ -53,6 +53,8 @@ class MediaPlayerPrivateGStreamerMSE : public MediaPlayerPrivateGStreamer { void load(const String&) override; void load(const String&, MediaSourcePrivateClient*) override; + FloatSize naturalSize() const final; + void setDownloadBuffering() override { }; bool isLiveStream() const override { return false; } @@ -79,6 +81,7 @@ class MediaPlayerPrivateGStreamerMSE : public MediaPlayerPrivateGStreamer { MediaSourcePrivateClient* mediaSourcePrivateClient() { return m_mediaSource.get(); } void markEndOfStream(MediaSourcePrivate::EndOfStreamStatus); + void unmarkEndOfStream(); void trackDetected(RefPtr, RefPtr oldTrack, RefPtr newTrack); void notifySeekNeedsDataForTime(const MediaTime&); @@ -107,6 +110,7 @@ class MediaPlayerPrivateGStreamerMSE : public MediaPlayerPrivateGStreamer { // FIXME: Implement. std::optional videoPlaybackQualityMetrics() override { return std::nullopt; } bool isTimeBuffered(const MediaTime&) const; + bool playbackPipelineHasFutureData() const; bool isMediaSource() const override { return true; } diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourceClientGStreamerMSE.cpp b/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourceClientGStreamerMSE.cpp index cb6dff5f15043..58fdc47620032 100644 --- a/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourceClientGStreamerMSE.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourceClientGStreamerMSE.cpp @@ -154,6 +154,16 @@ void MediaSourceClientGStreamerMSE::markEndOfStream(MediaSourcePrivate::EndOfStr m_playerPrivate->markEndOfStream(status); } +void MediaSourceClientGStreamerMSE::unmarkEndOfStream() +{ + ASSERT(WTF::isMainThread()); + + if (!m_playerPrivate) + return; + + m_playerPrivate->unmarkEndOfStream(); +} + void MediaSourceClientGStreamerMSE::removedFromMediaSource(RefPtr sourceBufferPrivate) { ASSERT(WTF::isMainThread()); diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourceClientGStreamerMSE.h b/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourceClientGStreamerMSE.h index da798cfb777c2..69f04d0cf44e6 100644 --- a/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourceClientGStreamerMSE.h +++ b/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourceClientGStreamerMSE.h @@ -44,6 +44,7 @@ class MediaSourceClientGStreamerMSE : public RefCounted, const ContentType&); void durationChanged(const MediaTime&); void markEndOfStream(MediaSourcePrivate::EndOfStreamStatus); + void unmarkEndOfStream(); // From SourceBufferPrivateGStreamer. void abort(RefPtr); diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourceGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourceGStreamer.cpp index e62cd56a846b3..7f63aeefac071 100644 --- a/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourceGStreamer.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourceGStreamer.cpp @@ -99,7 +99,7 @@ void MediaSourceGStreamer::markEndOfStream(EndOfStreamStatus status) void MediaSourceGStreamer::unmarkEndOfStream() { - notImplemented(); + m_client->unmarkEndOfStream(); } MediaPlayer::ReadyState MediaSourceGStreamer::readyState() const diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/PlaybackPipeline.cpp b/Source/WebCore/platform/graphics/gstreamer/mse/PlaybackPipeline.cpp index 0247f56457c14..dc5dfb174f142 100644 --- a/Source/WebCore/platform/graphics/gstreamer/mse/PlaybackPipeline.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/mse/PlaybackPipeline.cpp @@ -36,7 +36,6 @@ #include #include #include -#include #include #include #include @@ -114,7 +113,7 @@ MediaSourcePrivate::AddStatus PlaybackPipeline::addSourceBuffer(RefPtrparent = m_webKitMediaSrc.get(); stream->appsrc = gst_element_factory_make("appsrc", nullptr); stream->appsrcNeedDataFlag = false; @@ -128,12 +127,13 @@ MediaSourcePrivate::AddStatus PlaybackPipeline::addSourceBuffer(RefPtrvideoTrack = nullptr; stream->presentationSize = WebCore::FloatSize(); stream->lastEnqueuedTime = MediaTime::invalidTime(); + stream->firstEnqueuedTime = MediaTime::invalidTime(); gst_app_src_set_callbacks(GST_APP_SRC(stream->appsrc), &enabledAppsrcCallbacks, stream->parent, nullptr); gst_app_src_set_emit_signals(GST_APP_SRC(stream->appsrc), FALSE); gst_app_src_set_stream_type(GST_APP_SRC(stream->appsrc), GST_APP_STREAM_TYPE_SEEKABLE); - gst_app_src_set_max_bytes(GST_APP_SRC(stream->appsrc), 2 * WTF::MB); + gst_app_src_set_max_bytes(GST_APP_SRC(stream->appsrc), 8 * WTF::MB); g_object_set(G_OBJECT(stream->appsrc), "block", FALSE, "min-percent", 20, "format", GST_FORMAT_TIME, nullptr); GST_OBJECT_LOCK(m_webKitMediaSrc.get()); @@ -404,9 +404,15 @@ void PlaybackPipeline::flush(AtomicString trackId) } stream->lastEnqueuedTime = MediaTime::invalidTime(); + stream->firstEnqueuedTime = MediaTime::invalidTime(); GstElement* appsrc = stream->appsrc; GST_OBJECT_UNLOCK(m_webKitMediaSrc.get()); + if (trackId.startsWith("A")) { + GST_DEBUG("flush: refusing to flush audio stream"); + return; + } + if (!appsrc) return; @@ -417,7 +423,7 @@ void PlaybackPipeline::flush(AtomicString trackId) GST_TRACE("Position: %" GST_TIME_FORMAT, GST_TIME_ARGS(position)); - if (static_cast(position) == GST_CLOCK_TIME_NONE) { + if (!GST_CLOCK_TIME_IS_VALID(position)) { GST_TRACE("Can't determine position, avoiding flush"); return; } @@ -480,7 +486,6 @@ void PlaybackPipeline::enqueueSample(Ref&& mediaSample) GST_TIME_ARGS(WebCore::toGstClockTime(mediaSample->presentationTime().toDouble())), GST_TIME_ARGS(WebCore::toGstClockTime(mediaSample->duration().toDouble()))); - WTF::GMutexLocker locker(*GST_OBJECT_GET_LOCK(m_webKitMediaSrc.get())); Stream* stream = getStreamByTrackId(m_webKitMediaSrc.get(), trackId); if (!stream) { @@ -507,6 +512,8 @@ void PlaybackPipeline::enqueueSample(Ref&& mediaSample) // gst_app_src_push_sample() uses transfer-none for gstSample. stream->lastEnqueuedTime = lastEnqueuedTime; + if (!stream->firstEnqueuedTime.isValid()) + stream->firstEnqueuedTime = lastEnqueuedTime; } } @@ -518,6 +525,33 @@ GstElement* PlaybackPipeline::pipeline() return GST_ELEMENT_PARENT(GST_ELEMENT_PARENT(GST_ELEMENT(m_webKitMediaSrc.get()))); } +bool PlaybackPipeline::hasFutureData(const MediaTime& start) +{ + if (!m_webKitMediaSrc) + return false; + + MediaTime lastEnqueuedTime = MediaTime::positiveInfiniteTime(); + MediaTime firstEnqueuedTime = MediaTime::negativeInfiniteTime(); + + GST_OBJECT_LOCK(m_webKitMediaSrc.get()); + WebKitMediaSrcPrivate* priv = m_webKitMediaSrc->priv; + for (Stream* stream : priv->streams) { + if (lastEnqueuedTime > stream->lastEnqueuedTime) + lastEnqueuedTime = stream->lastEnqueuedTime; + if (firstEnqueuedTime < stream->firstEnqueuedTime) + firstEnqueuedTime = stream->firstEnqueuedTime; + } + GST_OBJECT_UNLOCK(m_webKitMediaSrc.get()); + + if (lastEnqueuedTime.isPositiveInfinite()) + return false; + + const MediaTime threshold = MediaTime(350, 1000); + MediaTime end = start + threshold; + + return firstEnqueuedTime <= start && lastEnqueuedTime > end; +} + } // namespace WebCore. #endif // USE(GSTREAMER) diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/PlaybackPipeline.h b/Source/WebCore/platform/graphics/gstreamer/mse/PlaybackPipeline.h index da498d9fb6fc1..c1cced4c916c9 100644 --- a/Source/WebCore/platform/graphics/gstreamer/mse/PlaybackPipeline.h +++ b/Source/WebCore/platform/graphics/gstreamer/mse/PlaybackPipeline.h @@ -68,6 +68,8 @@ class PlaybackPipeline: public RefCounted { void flush(AtomicString); void enqueueSample(Ref&&); + bool hasFutureData(const MediaTime& start); + GstElement* pipeline(); private: PlaybackPipeline() = default; diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/WebKitMediaSourceGStreamerPrivate.h b/Source/WebCore/platform/graphics/gstreamer/mse/WebKitMediaSourceGStreamerPrivate.h index 51117ccbaf893..bfc7a147f194c 100644 --- a/Source/WebCore/platform/graphics/gstreamer/mse/WebKitMediaSourceGStreamerPrivate.h +++ b/Source/WebCore/platform/graphics/gstreamer/mse/WebKitMediaSourceGStreamerPrivate.h @@ -69,6 +69,7 @@ struct _Stream { // Used to enforce continuity in the appended data and avoid breaking the decoder. MediaTime lastEnqueuedTime; + MediaTime firstEnqueuedTime; }; enum { diff --git a/Source/WebInspectorUI/UserInterface/Base/Main.js b/Source/WebInspectorUI/UserInterface/Base/Main.js index cc4e9ba414462..b97add5868d37 100644 --- a/Source/WebInspectorUI/UserInterface/Base/Main.js +++ b/Source/WebInspectorUI/UserInterface/Base/Main.js @@ -203,7 +203,7 @@ WI.loaded = function() y: 0 }; - this.visible = false; + this.visible = true; this._windowKeydownListeners = []; }; diff --git a/Source/WebInspectorUI/UserInterface/Views/DebuggerSidebarPanel.css b/Source/WebInspectorUI/UserInterface/Views/DebuggerSidebarPanel.css index e84cd763a3212..99dd24f6fcc6d 100644 --- a/Source/WebInspectorUI/UserInterface/Views/DebuggerSidebarPanel.css +++ b/Source/WebInspectorUI/UserInterface/Views/DebuggerSidebarPanel.css @@ -28,7 +28,6 @@ } .sidebar > .panel.navigation.debugger > .navigation-bar { - position: absolute; top: 0; left: 0; right: 0; diff --git a/Source/WebInspectorUI/UserInterface/Views/DebuggerSidebarPanel.js b/Source/WebInspectorUI/UserInterface/Views/DebuggerSidebarPanel.js index ab26c2a719a72..bedf1e2d8b755 100644 --- a/Source/WebInspectorUI/UserInterface/Views/DebuggerSidebarPanel.js +++ b/Source/WebInspectorUI/UserInterface/Views/DebuggerSidebarPanel.js @@ -68,7 +68,7 @@ WI.DebuggerSidebarPanel = class DebuggerSidebarPanel extends WI.NavigationSideba enableBreakpointsLink.addEventListener("click", () => { WI.debuggerToggleBreakpoints(); }); this._navigationBar = new WI.NavigationBar; - this.addSubview(this._navigationBar); + this.insertSubviewBefore(this._navigationBar, this._contentView); var breakpointsImage = {src: "Images/Breakpoints.svg", width: 15, height: 15}; var pauseImage = {src: "Images/Pause.svg", width: 15, height: 15}; diff --git a/Source/WebInspectorUI/UserInterface/Views/LegacyNetworkSidebarPanel.css b/Source/WebInspectorUI/UserInterface/Views/LegacyNetworkSidebarPanel.css index 60f7b7dfc6019..d6b3530bc9348 100644 --- a/Source/WebInspectorUI/UserInterface/Views/LegacyNetworkSidebarPanel.css +++ b/Source/WebInspectorUI/UserInterface/Views/LegacyNetworkSidebarPanel.css @@ -28,14 +28,12 @@ } .sidebar > .panel.navigation.network > .navigation-bar { - position: absolute; top: 0; left: 0; right: 0; } .sidebar > .panel.navigation.network > .title-bar { - position: absolute; top: var(--navigation-bar-height); left: 0; right: 0; diff --git a/Source/WebInspectorUI/UserInterface/Views/LegacyNetworkSidebarPanel.js b/Source/WebInspectorUI/UserInterface/Views/LegacyNetworkSidebarPanel.js index 5479a8a43a186..9f0fb5b46d7b9 100644 --- a/Source/WebInspectorUI/UserInterface/Views/LegacyNetworkSidebarPanel.js +++ b/Source/WebInspectorUI/UserInterface/Views/LegacyNetworkSidebarPanel.js @@ -68,12 +68,12 @@ WI.LegacyNetworkSidebarPanel = class LegacyNetworkSidebarPanel extends WI.Naviga initialLayout() { this._navigationBar = new WI.NavigationBar; - this.addSubview(this._navigationBar); + this.insertSubviewBefore(this._navigationBar, this._contentView); this._resourcesTitleBarElement = document.createElement("div"); this._resourcesTitleBarElement.textContent = WI.UIString("Name"); this._resourcesTitleBarElement.classList.add("title-bar"); - this.element.appendChild(this._resourcesTitleBarElement); + this.element.insertBefore(this._resourcesTitleBarElement, this._contentView.element); let scopeItemPrefix = "network-sidebar-"; let scopeBarItems = []; diff --git a/Source/WebInspectorUI/UserInterface/Views/NavigationSidebarPanel.css b/Source/WebInspectorUI/UserInterface/Views/NavigationSidebarPanel.css index e51be5e1cbd31..dbcad7547ce72 100644 --- a/Source/WebInspectorUI/UserInterface/Views/NavigationSidebarPanel.css +++ b/Source/WebInspectorUI/UserInterface/Views/NavigationSidebarPanel.css @@ -60,10 +60,8 @@ } .sidebar > .panel.navigation > .content > .empty-content-placeholder { - position: absolute; top: 0; bottom: 0; - padding: 0; } .sidebar > .panel.navigation > .content .empty-content-placeholder > .message { diff --git a/Source/WebInspectorUI/UserInterface/Views/Popover.css b/Source/WebInspectorUI/UserInterface/Views/Popover.css index 4d5db4376f36d..909f4b5f0b928 100644 --- a/Source/WebInspectorUI/UserInterface/Views/Popover.css +++ b/Source/WebInspectorUI/UserInterface/Views/Popover.css @@ -24,6 +24,8 @@ */ .popover { + background-color: #EEEEEE; + border: 1px solid darkgrey; position: absolute; min-width: 20px; min-height: 20px; diff --git a/Source/WebInspectorUI/UserInterface/Views/Popover.js b/Source/WebInspectorUI/UserInterface/Views/Popover.js index bef8e7155cb30..cf80a0036a1ef 100644 --- a/Source/WebInspectorUI/UserInterface/Views/Popover.js +++ b/Source/WebInspectorUI/UserInterface/Views/Popover.js @@ -433,7 +433,12 @@ WI.Popover = class Popover extends WI.Object ctx.stroke(); // Draw the popover into the final context with a drop shadow. - let finalContext = document.getCSSCanvasContext("2d", "popover", scaledWidth, scaledHeight); + var popoverCanvas = document.createElement("canvas"); + popoverCanvas.width = scaledWidth; + popoverCanvas.height = scaledHeight; + + var finalContext = popoverCanvas.getContext("2d"); + finalContext.clearRect(0, 0, scaledWidth, scaledHeight); finalContext.shadowOffsetX = 1; finalContext.shadowOffsetY = 1; diff --git a/Source/WebInspectorUI/UserInterface/Views/ResourceSidebarPanel.css b/Source/WebInspectorUI/UserInterface/Views/ResourceSidebarPanel.css index 3109735dffad9..d86b8ff14621e 100644 --- a/Source/WebInspectorUI/UserInterface/Views/ResourceSidebarPanel.css +++ b/Source/WebInspectorUI/UserInterface/Views/ResourceSidebarPanel.css @@ -28,7 +28,6 @@ } .sidebar > .panel.navigation.resource > .navigation-bar { - position: absolute; top: 0; left: 0; right: 0; diff --git a/Source/WebInspectorUI/UserInterface/Views/ResourceSidebarPanel.js b/Source/WebInspectorUI/UserInterface/Views/ResourceSidebarPanel.js index 6def4eda4236b..03cf8be4411dc 100644 --- a/Source/WebInspectorUI/UserInterface/Views/ResourceSidebarPanel.js +++ b/Source/WebInspectorUI/UserInterface/Views/ResourceSidebarPanel.js @@ -32,7 +32,7 @@ WI.ResourceSidebarPanel = class ResourceSidebarPanel extends WI.NavigationSideba this.contentBrowser = contentBrowser; this._navigationBar = new WI.NavigationBar; - this.addSubview(this._navigationBar); + this.insertSubviewBefore(this._navigationBar, this._contentView); this._targetTreeElementMap = new Map; diff --git a/Source/WebInspectorUI/UserInterface/Views/SidebarPanel.css b/Source/WebInspectorUI/UserInterface/Views/SidebarPanel.css index 15e8dcb1a72d5..f8a6a803622f4 100644 --- a/Source/WebInspectorUI/UserInterface/Views/SidebarPanel.css +++ b/Source/WebInspectorUI/UserInterface/Views/SidebarPanel.css @@ -24,7 +24,6 @@ */ .sidebar > .panel > .content { - position: absolute; top: 0; left: 0; right: 0; diff --git a/Source/WebInspectorUI/UserInterface/Views/StorageSidebarPanel.css b/Source/WebInspectorUI/UserInterface/Views/StorageSidebarPanel.css index d418a9f4aabb2..410b0ec40ee06 100644 --- a/Source/WebInspectorUI/UserInterface/Views/StorageSidebarPanel.css +++ b/Source/WebInspectorUI/UserInterface/Views/StorageSidebarPanel.css @@ -28,7 +28,6 @@ } .sidebar > .panel.navigation.storage > .navigation-bar { - position: absolute; top: 0; left: 0; right: 0; diff --git a/Source/WebInspectorUI/UserInterface/Views/StorageSidebarPanel.js b/Source/WebInspectorUI/UserInterface/Views/StorageSidebarPanel.js index c4c0ed987379c..f60469a222a96 100644 --- a/Source/WebInspectorUI/UserInterface/Views/StorageSidebarPanel.js +++ b/Source/WebInspectorUI/UserInterface/Views/StorageSidebarPanel.js @@ -32,7 +32,7 @@ WI.StorageSidebarPanel = class StorageSidebarPanel extends WI.NavigationSidebarP this.contentBrowser = contentBrowser; this._navigationBar = new WI.NavigationBar; - this.addSubview(this._navigationBar); + this.insertSubviewBefore(this._navigationBar, this._contentView); var scopeItemPrefix = "storage-sidebar-"; var scopeBarItems = []; diff --git a/Source/WebKit/Shared/API/APIURLRequest.cpp b/Source/WebKit/Shared/API/APIURLRequest.cpp index ac904d409c215..0aa1541676224 100644 --- a/Source/WebKit/Shared/API/APIURLRequest.cpp +++ b/Source/WebKit/Shared/API/APIURLRequest.cpp @@ -68,4 +68,9 @@ bool URLRequest::decode(IPC::Decoder& decoder, RefPtr& result) return true; } +void URLRequest::setHTTPHeaderField(const WTF::String& key, const WTF::String& value) +{ + m_request.setHTTPHeaderField(key, value); +} + } // namespace API diff --git a/Source/WebKit/Shared/API/APIURLRequest.h b/Source/WebKit/Shared/API/APIURLRequest.h index c400e8e90ff41..08a45b73dc062 100644 --- a/Source/WebKit/Shared/API/APIURLRequest.h +++ b/Source/WebKit/Shared/API/APIURLRequest.h @@ -44,6 +44,7 @@ class URLRequest : public ObjectImpl { } const WebCore::ResourceRequest& resourceRequest() const { return m_request; } + void setHTTPHeaderField(const WTF::String&, const WTF::String&); static double defaultTimeoutInterval(); // May return 0 when using platform default. static void setDefaultTimeoutInterval(double); diff --git a/Source/WebKit/Shared/API/c/WKURLRequest.cpp b/Source/WebKit/Shared/API/c/WKURLRequest.cpp index 736e1f3e6645c..0da718b34e7f1 100644 --- a/Source/WebKit/Shared/API/c/WKURLRequest.cpp +++ b/Source/WebKit/Shared/API/c/WKURLRequest.cpp @@ -70,3 +70,8 @@ void WKURLRequestSetDefaultTimeoutInterval(double timeoutInterval) { API::URLRequest::setDefaultTimeoutInterval(timeoutInterval); } + +void WKURLRequestSetHTTPHeaderField(WKURLRequestRef requestRef, WKStringRef key, WKStringRef value) +{ + toImpl(requestRef)->setHTTPHeaderField(toImpl(key)->string(), toImpl(value)->string()); +} diff --git a/Source/WebKit/Shared/API/c/WKURLRequest.h b/Source/WebKit/Shared/API/c/WKURLRequest.h index 174180423d6b4..0a4ff0ebba232 100644 --- a/Source/WebKit/Shared/API/c/WKURLRequest.h +++ b/Source/WebKit/Shared/API/c/WKURLRequest.h @@ -23,8 +23,7 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef WKURLRequest_h -#define WKURLRequest_h +#pragma once #include @@ -46,8 +45,8 @@ WK_EXPORT WKURLRequestRef WKURLRequestCopySettingHTTPBody(WKURLRequestRef, WKDat WK_EXPORT void WKURLRequestSetDefaultTimeoutInterval(double); +WK_EXPORT void WKURLRequestSetHTTPHeaderField(WKURLRequestRef, WKStringRef, WKStringRef); + #ifdef __cplusplus } #endif - -#endif /* WKURLRequest_h */ diff --git a/Source/WebKit/UIProcess/API/APIWebProxy.h b/Source/WebKit/UIProcess/API/APIWebProxy.h index b0e48c8ed3a91..58c3e20ba1cd1 100644 --- a/Source/WebKit/UIProcess/API/APIWebProxy.h +++ b/Source/WebKit/UIProcess/API/APIWebProxy.h @@ -23,8 +23,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef WebProxy_h -#define WeProxy_h +#pragma once #include "APIObject.h" #include @@ -43,11 +42,9 @@ class WebProxy final : public API::ObjectImpl { private: WebProxy(const WebCore::Proxy& proxy) - : m_proxy(proxy) {} + : m_proxy(proxy) { } WebCore::Proxy m_proxy; }; } - -#endif diff --git a/Source/WebKit/UIProcess/API/C/WKPage.cpp b/Source/WebKit/UIProcess/API/C/WKPage.cpp index 0ad630a7406db..9b8e7754eeb2a 100644 --- a/Source/WebKit/UIProcess/API/C/WKPage.cpp +++ b/Source/WebKit/UIProcess/API/C/WKPage.cpp @@ -224,12 +224,12 @@ void WKPageLoadAlternateHTMLStringWithUserData(WKPageRef pageRef, WKStringRef ht void WKPageLoadPlainTextString(WKPageRef pageRef, WKStringRef plainTextStringRef) { - toImpl(pageRef)->loadPlainTextString(toWTFString(plainTextStringRef)); + toImpl(pageRef)->loadPlainTextString(toWTFString(plainTextStringRef)); } void WKPageLoadPlainTextStringWithUserData(WKPageRef pageRef, WKStringRef plainTextStringRef, WKTypeRef userDataRef) { - toImpl(pageRef)->loadPlainTextString(toWTFString(plainTextStringRef), toImpl(userDataRef)); + toImpl(pageRef)->loadPlainTextString(toWTFString(plainTextStringRef), toImpl(userDataRef)); } void WKPageLoadWebArchiveData(WKPageRef pageRef, WKDataRef webArchiveDataRef) @@ -963,7 +963,7 @@ void WKPageSetPageFindClient(WKPageRef pageRef, const WKPageFindClientBase* wkCl { if (!m_client.didFindString) return; - + m_client.didFindString(toAPI(page), toAPI(string.impl()), matchCount, m_client.base.clientInfo); } @@ -971,7 +971,7 @@ void WKPageSetPageFindClient(WKPageRef pageRef, const WKPageFindClientBase* wkCl { if (!m_client.didFailToFindString) return; - + m_client.didFailToFindString(toAPI(page), toAPI(string.impl()), m_client.base.clientInfo); } @@ -1326,7 +1326,7 @@ void WKPageSetPageLoaderClient(WKPageRef pageRef, const WKPageLoaderClientBase* RefPtr webUnavailabilityDescription = adoptRef(toImpl(unavailabilityDescriptionOut)); unavailabilityDescription = webUnavailabilityDescription->string(); } - + return loadPolicy; } #endif // ENABLE(NETSCAPE_PLUGIN_API) @@ -1434,7 +1434,7 @@ void WKPageSetPagePolicyClient(WKPageRef pageRef, const WKPagePolicyClientBase* { if (!m_client.unableToImplementPolicy) return; - + m_client.unableToImplementPolicy(toAPI(&page), toAPI(&frame), toAPI(error), toAPI(userData), m_client.base.clientInfo); } }; @@ -1490,7 +1490,7 @@ class RunJavaScriptAlertResultListener : public API::ObjectImpl m_completionHandler; }; @@ -1615,7 +1615,7 @@ void WKPageSetPageUIClient(WKPageRef pageRef, const WKPageUIClientBase* wkClient return completionHandler(adoptRef(toImpl(m_client.createNewPage(toAPI(&page), toAPI(configuration.ptr()), toAPI(apiNavigationAction.ptr()), toAPI(apiWindowFeatures.ptr()), m_client.base.clientInfo)))); } - + if (m_client.createNewPage_deprecatedForUseWithV1 || m_client.createNewPage_deprecatedForUseWithV0) { API::Dictionary::MapType map; if (windowFeatures.x) @@ -1640,7 +1640,7 @@ void WKPageSetPageUIClient(WKPageRef pageRef, const WKPageUIClientBase* wkClient Ref request = API::URLRequest::create(resourceRequest); return completionHandler(adoptRef(toImpl(m_client.createNewPage_deprecatedForUseWithV1(toAPI(&page), toAPI(request.ptr()), toAPI(featuresMap.ptr()), toAPI(navigationActionData.modifiers), toAPI(navigationActionData.mouseButton), m_client.base.clientInfo)))); } - + ASSERT(m_client.createNewPage_deprecatedForUseWithV0); return completionHandler(adoptRef(toImpl(m_client.createNewPage_deprecatedForUseWithV0(toAPI(&page), toAPI(featuresMap.ptr()), toAPI(navigationActionData.modifiers), toAPI(navigationActionData.mouseButton), m_client.base.clientInfo)))); } @@ -1727,7 +1727,7 @@ void WKPageSetPageUIClient(WKPageRef pageRef, const WKPageUIClientBase* wkClient completionHandler(); return; } - + if (m_client.runJavaScriptAlert_deprecatedForUseWithV0) { m_client.runJavaScriptAlert_deprecatedForUseWithV0(toAPI(page), toAPI(message.impl()), toAPI(frame), m_client.base.clientInfo); completionHandler(); @@ -1750,18 +1750,18 @@ void WKPageSetPageUIClient(WKPageRef pageRef, const WKPageUIClientBase* wkClient if (m_client.runJavaScriptConfirm_deprecatedForUseWithV5) { RefPtr securityOrigin = API::SecurityOrigin::create(securityOriginData.protocol, securityOriginData.host, securityOriginData.port); bool result = m_client.runJavaScriptConfirm_deprecatedForUseWithV5(toAPI(page), toAPI(message.impl()), toAPI(frame), toAPI(securityOrigin.get()), m_client.base.clientInfo); - + completionHandler(result); return; } - + if (m_client.runJavaScriptConfirm_deprecatedForUseWithV0) { bool result = m_client.runJavaScriptConfirm_deprecatedForUseWithV0(toAPI(page), toAPI(message.impl()), toAPI(frame), m_client.base.clientInfo); completionHandler(result); return; } - + completionHandler(false); } @@ -1777,17 +1777,17 @@ void WKPageSetPageUIClient(WKPageRef pageRef, const WKPageUIClientBase* wkClient if (m_client.runJavaScriptPrompt_deprecatedForUseWithV5) { RefPtr securityOrigin = API::SecurityOrigin::create(securityOriginData.protocol, securityOriginData.host, securityOriginData.port); RefPtr string = adoptRef(toImpl(m_client.runJavaScriptPrompt_deprecatedForUseWithV5(toAPI(page), toAPI(message.impl()), toAPI(defaultValue.impl()), toAPI(frame), toAPI(securityOrigin.get()), m_client.base.clientInfo))); - + if (string) completionHandler(string->string()); else completionHandler(String()); return; } - + if (m_client.runJavaScriptPrompt_deprecatedForUseWithV0) { RefPtr string = adoptRef(toImpl(m_client.runJavaScriptPrompt_deprecatedForUseWithV0(toAPI(page), toAPI(message.impl()), toAPI(defaultValue.impl()), toAPI(frame), m_client.base.clientInfo))); - + if (string) completionHandler(string->string()); else @@ -2120,7 +2120,7 @@ void WKPageSetPageUIClient(WKPageRef pageRef, const WKPageUIClientBase* wkClient #endif void willAddDetailedMessageToConsole(WebPageProxy* page, const String& source, const String& level, - uint64_t line, uint64_t column, const String& message, const String& url) + uint64_t line, uint64_t column, const String& message, const String& url) override { if (!m_client.willAddDetailedMessageToConsole) return; @@ -2274,21 +2274,21 @@ void WKPageSetPageNavigationClient(WKPageRef pageRef, const WKPageNavigationClie return; m_client.didSameDocumentNavigation(toAPI(&page), toAPI(navigation), toAPI(navigationType), toAPI(userData), m_client.base.clientInfo); } - + void renderingProgressDidChange(WebPageProxy& page, WebCore::LayoutMilestones milestones) override { if (!m_client.renderingProgressDidChange) return; m_client.renderingProgressDidChange(toAPI(&page), pageRenderingProgressEvents(milestones), nullptr, m_client.base.clientInfo); } - + bool canAuthenticateAgainstProtectionSpace(WebPageProxy& page, WebProtectionSpace* protectionSpace) override { if (!m_client.canAuthenticateAgainstProtectionSpace) return false; return m_client.canAuthenticateAgainstProtectionSpace(toAPI(&page), toAPI(protectionSpace), m_client.base.clientInfo); } - + void didReceiveAuthenticationChallenge(WebPageProxy& page, AuthenticationChallengeProxy* authenticationChallenge) override { if (!m_client.didReceiveAuthenticationChallenge) @@ -2348,21 +2348,21 @@ void WKPageSetPageNavigationClient(WKPageRef pageRef, const WKPageNavigationClie return; m_client.didRemoveNavigationGestureSnapshot(toAPI(&page), m_client.base.clientInfo); } - + #if ENABLE(NETSCAPE_PLUGIN_API) PluginModuleLoadPolicy decidePolicyForPluginLoad(WebPageProxy& page, PluginModuleLoadPolicy currentPluginLoadPolicy, API::Dictionary* pluginInformation, String& unavailabilityDescription) override { WKStringRef unavailabilityDescriptionOut = 0; PluginModuleLoadPolicy loadPolicy = currentPluginLoadPolicy; - + if (m_client.decidePolicyForPluginLoad) loadPolicy = toPluginModuleLoadPolicy(m_client.decidePolicyForPluginLoad(toAPI(&page), toWKPluginLoadPolicy(currentPluginLoadPolicy), toAPI(pluginInformation), &unavailabilityDescriptionOut, m_client.base.clientInfo)); - + if (unavailabilityDescriptionOut) { RefPtr webUnavailabilityDescription = adoptRef(toImpl(unavailabilityDescriptionOut)); unavailabilityDescription = webUnavailabilityDescription->string(); } - + return loadPolicy; } #endif @@ -2552,7 +2552,7 @@ bool WKPageGetAllowsRemoteInspection(WKPageRef page) #else UNUSED_PARAM(page); return false; -#endif +#endif } void WKPageSetAllowsRemoteInspection(WKPageRef page, bool allow) @@ -2567,7 +2567,7 @@ void WKPageSetAllowsRemoteInspection(WKPageRef page, bool allow) void WKPageSetMediaVolume(WKPageRef page, float volume) { - toImpl(page)->setMediaVolume(volume); + toImpl(page)->setMediaVolume(volume); } void WKPageSetMuted(WKPageRef page, WKMediaMutedState muted) @@ -2700,7 +2700,7 @@ void WKPageSelectContextMenuItem(WKPageRef page, WKContextMenuItemRef item) WKScrollPinningBehavior WKPageGetScrollPinningBehavior(WKPageRef page) { ScrollPinningBehavior pinning = toImpl(page)->scrollPinningBehavior(); - + switch (pinning) { case WebCore::ScrollPinningBehavior::DoNotPin: return kWKScrollPinningBehaviorDoNotPin; @@ -2709,7 +2709,7 @@ WKScrollPinningBehavior WKPageGetScrollPinningBehavior(WKPageRef page) case WebCore::ScrollPinningBehavior::PinToBottom: return kWKScrollPinningBehaviorPinToBottom; } - + ASSERT_NOT_REACHED(); return kWKScrollPinningBehaviorDoNotPin; } @@ -2731,7 +2731,7 @@ void WKPageSetScrollPinningBehavior(WKPageRef page, WKScrollPinningBehavior pinn default: ASSERT_NOT_REACHED(); } - + toImpl(page)->setScrollPinningBehavior(corePinning); } diff --git a/Source/WebKit/UIProcess/API/wpe/PageClientImpl.cpp b/Source/WebKit/UIProcess/API/wpe/PageClientImpl.cpp index 5ab9e43cea28c..fabfbec415310 100644 --- a/Source/WebKit/UIProcess/API/wpe/PageClientImpl.cpp +++ b/Source/WebKit/UIProcess/API/wpe/PageClientImpl.cpp @@ -306,7 +306,7 @@ void PageClientImpl::derefView() { } -#if ENABLE(VIDEO) +#if ENABLE(VIDEO) && USE(GSTREAMER) bool PageClientImpl::decidePolicyForInstallMissingMediaPluginsPermissionRequest(InstallMissingMediaPluginsPermissionRequest&) { return false; diff --git a/Source/WebKit/UIProcess/API/wpe/WPEView.cpp b/Source/WebKit/UIProcess/API/wpe/WPEView.cpp index 182b40f9b0d28..7e66c93273fa7 100644 --- a/Source/WebKit/UIProcess/API/wpe/WPEView.cpp +++ b/Source/WebKit/UIProcess/API/wpe/WPEView.cpp @@ -69,6 +69,11 @@ View::View(struct wpe_view_backend* backend, const API::PageConfiguration& baseC auto* pool = configuration->processPool(); m_pageProxy = pool->createWebPage(*m_pageClient, WTFMove(configuration)); +#if ENABLE(MEMORY_SAMPLER) + if (getenv("WEBKIT_SAMPLE_MEMORY")) + pool->startMemorySampler(0); +#endif + #if PLATFORM(INTEL_CE) m_pageProxy->setDrawsBackground(false); #endif diff --git a/Source/WebKit/WebProcess/WebPage/wpe/WebPageWPE.cpp b/Source/WebKit/WebProcess/WebPage/wpe/WebPageWPE.cpp index 63264041cf746..aa97cebe09a27 100644 --- a/Source/WebKit/WebProcess/WebPage/wpe/WebPageWPE.cpp +++ b/Source/WebKit/WebProcess/WebPage/wpe/WebPageWPE.cpp @@ -29,6 +29,7 @@ #include "NotImplemented.h" #include "WebPreferencesKeys.h" #include "WebPreferencesStore.h" +#include "WindowsKeyboardCodes.h" #include #include @@ -55,10 +56,23 @@ void WebPage::platformPreferencesDidChange(const WebPreferencesStore& store) m_page->settings().setAllowDisplayOfInsecureContent(store.getBoolValueForKey(WebPreferencesKey::allowDisplayOfInsecureContentKey())); } -bool WebPage::performDefaultBehaviorForKeyEvent(const WebKeyboardEvent&) +bool WebPage::performDefaultBehaviorForKeyEvent(const WebKeyboardEvent& keyboardEvent) { - notImplemented(); - return false; + if (keyboardEvent.type() != WebEvent::KeyDown && keyboardEvent.type() != WebEvent::RawKeyDown) + return false; + + switch (keyboardEvent.windowsVirtualKeyCode()) { + case VK_PRIOR: + scroll(m_page.get(), ScrollUp, ScrollByPage); + break; + case VK_NEXT: + scroll(m_page.get(), ScrollDown, ScrollByPage); + break; + default: + return false; + } + + return true; } bool WebPage::platformHasLocalDataForURL(const URL&) diff --git a/Source/WebKit2/Shared/unix/BreakpadExceptionHandler.h b/Source/WebKit2/Shared/unix/BreakpadExceptionHandler.h deleted file mode 100644 index a4b5d8fe04b8a..0000000000000 --- a/Source/WebKit2/Shared/unix/BreakpadExceptionHandler.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef BreakpadExceptionHandler_h -#define BreakpadExceptionHandler_h - -#if defined (USE_BREAKPAD) -#include "config.h" -#include - -namespace -{ -// called by 'google_breakpad::ExceptionHandler' on every crash -bool breakpadCallback(const google_breakpad::MinidumpDescriptor& descriptor, void* context, bool succeeded) -{ - (void) descriptor; - (void) context; - return succeeded; -} - -void installExceptionHandler() -{ - static google_breakpad::ExceptionHandler* excHandler = NULL; - delete excHandler; - const char* BREAKPAD_MINIDUMP_DIR = "/opt/minidumps"; - excHandler = new google_breakpad::ExceptionHandler(google_breakpad::MinidumpDescriptor(BREAKPAD_MINIDUMP_DIR), NULL, breakpadCallback, NULL, true, -1); -} -} -#endif - -#endif // BreakpadExceptionHandler_h diff --git a/Source/WebKit2/UIProcess/InspectorServer/wpe/WebInspectorServerWPE.cpp b/Source/WebKit2/UIProcess/InspectorServer/wpe/WebInspectorServerWPE.cpp deleted file mode 100644 index 2683e3d7160cb..0000000000000 --- a/Source/WebKit2/UIProcess/InspectorServer/wpe/WebInspectorServerWPE.cpp +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (C) 2012 Samsung Electronics Ltd. 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. - */ - -#include "config.h" -#include "WebInspectorServer.h" - -#if ENABLE(INSPECTOR_SERVER) - -#include "WebInspectorProxy.h" -#include "WebPageProxy.h" -#include -#include -#include -#include -#include -#include - -namespace WebKit { - -bool WebInspectorServer::platformResourceForPath(const String& path, Vector& data, String& contentType) -{ - // The page list contains an unformated list of pages that can be inspected with a link to open a session. - if (path == "/pagelist.json") { - buildPageList(data, contentType); - return true; - } - - static std::once_flag flag; - std::call_once(flag, [] { - GModule* resourcesModule = g_module_open("libWPEWebInspectorResources.so", G_MODULE_BIND_LAZY); - if (!resourcesModule) { - WTFLogAlways("Error loading libWPEWebInspectorResources.so: %s", g_module_error()); - return; - } - - g_module_make_resident(resourcesModule); - }); - - // Point the default path to a formatted page that queries the page list and display them. - CString resourcePath = makeString("/org/wpe/inspector/UserInterface", (path == "/" ? "/inspectorPageIndex.html" : path)).utf8(); - if (resourcePath.isNull()) - return false; - - GUniqueOutPtr error; - GRefPtr resourceBytes = adoptGRef(g_resources_lookup_data(resourcePath.data(), G_RESOURCE_LOOKUP_FLAGS_NONE, &error.outPtr())); - if (!resourceBytes) { - StringBuilder builder; - builder.appendLiteral("Error: "); - builder.appendNumber(error->code); - builder.appendLiteral(", "); - builder.append(error->message); - builder.appendLiteral(" occurred during fetching inspector resource files."); - - CString errorHTML = builder.toString().utf8(); - data.append(errorHTML.data(), errorHTML.length()); - contentType = "text/html; charset=utf-8"; - - WTFLogAlways("Error fetching webinspector resource files: %d, %s", error->code, error->message); - return false; - } - - gsize resourceDataSize; - gconstpointer resourceData = g_bytes_get_data(resourceBytes.get(), &resourceDataSize); - data.append(static_cast(resourceData), resourceDataSize); - - GUniquePtr mimeType(g_content_type_guess(resourcePath.data(), static_cast(resourceData), resourceDataSize, nullptr)); - contentType = mimeType.get(); - return true; -} - -void WebInspectorServer::buildPageList(Vector& data, String& contentType) -{ - // chromedevtools (http://code.google.com/p/chromedevtools) 0.3.8 expected JSON format: - // { - // "title": "Foo", - // "url": "http://foo", - // "devtoolsFrontendUrl": "/Main.html?ws=localhost:9222/devtools/page/1", - // "webSocketDebuggerUrl": "ws://localhost:9222/devtools/page/1" - // }, - - StringBuilder builder; - builder.appendLiteral("[ "); - ClientMap::iterator end = m_clientMap.end(); - for (ClientMap::iterator it = m_clientMap.begin(); it != end; ++it) { - WebPageProxy* webPage = it->value->inspectedPage(); - if (it != m_clientMap.begin()) - builder.appendLiteral(", "); - builder.appendLiteral("{ \"id\": "); - builder.appendNumber(it->key); - builder.appendLiteral(", \"title\": \""); - builder.append(webPage->pageLoadState().title()); - builder.appendLiteral("\", \"url\": \""); - builder.append(webPage->pageLoadState().activeURL()); - builder.appendLiteral("\", \"inspectorUrl\": \""); - builder.appendLiteral("/Main.html?page="); - builder.appendNumber(it->key); - builder.appendLiteral("\", \"devtoolsFrontendUrl\": \""); - builder.appendLiteral("/Main.html?ws="); - builder.append(bindAddress()); - builder.appendLiteral(":"); - builder.appendNumber(port()); - builder.appendLiteral("/devtools/page/"); - builder.appendNumber(it->key); - builder.appendLiteral("\", \"webSocketDebuggerUrl\": \""); - builder.appendLiteral("ws://"); - builder.append(bindAddress()); - builder.appendLiteral(":"); - builder.appendNumber(port()); - builder.appendLiteral("/devtools/page/"); - builder.appendNumber(it->key); - builder.appendLiteral("\" }"); - } - builder.appendLiteral(" ]"); - CString cstr = builder.toString().utf8(); - data.append(cstr.data(), cstr.length()); - contentType = "application/json; charset=utf-8"; -} - -} // namespace WebKit - -#endif diff --git a/Source/bmalloc/bmalloc/BPlatform.h b/Source/bmalloc/bmalloc/BPlatform.h index 84f31b28954d9..5686f2e8eb6d3 100644 --- a/Source/bmalloc/bmalloc/BPlatform.h +++ b/Source/bmalloc/bmalloc/BPlatform.h @@ -180,7 +180,9 @@ || defined(__ARM_ARCH_7K__) \ || defined(__ARM_ARCH_7M__) \ || defined(__ARM_ARCH_7R__) \ -|| defined(__ARM_ARCH_7S__) +|| defined(__ARM_ARCH_7S__) \ +|| defined(__ARM_ARCH_8__) \ +|| defined(__ARM_ARCH_8A__) #define BTHUMB_ARCH_VERSION 4 /* RVCT sets __TARGET_ARCH_THUMB */ diff --git a/Source/cmake/OptionsWPE.cmake b/Source/cmake/OptionsWPE.cmake index 987d50d5e55b0..013a927eb0be0 100644 --- a/Source/cmake/OptionsWPE.cmake +++ b/Source/cmake/OptionsWPE.cmake @@ -43,6 +43,7 @@ WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_TOUCH_EVENTS PUBLIC ON) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_USER_MESSAGE_HANDLERS PRIVATE ON) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_WEB_ANIMATIONS PRIVATE ON) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_WEBGL PUBLIC ON) +WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_MEMORY_SAMPLER PUBLIC ON) if (CMAKE_SYSTEM_NAME MATCHES "Linux") WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_MEMORY_SAMPLER PRIVATE ON)