void InbandDataTextTrack::updateDataCue(InbandTextTrackPrivate*, const MediaTime& start, const MediaTime& inEnd, PassRefPtr<SerializedPlatformRepresentation> prpPlatformValue) { MediaTime end = inEnd; RefPtr<SerializedPlatformRepresentation> platformValue = prpPlatformValue; auto iter = m_incompleteCueMap.find(platformValue.get()); if (iter == m_incompleteCueMap.end()) return; RefPtr<DataCue> cue = iter->value; if (!cue) return; cue->willChange(); if (end.isPositiveInfinite() && mediaElement()) end = mediaElement()->durationMediaTime(); else m_incompleteCueMap.remove(platformValue.get()); LOG(Media, "InbandDataTextTrack::updateDataCue: was start=%s, end=%s, will be start=%s, end=%s\n", toString(cue->startTime()).utf8().data(), toString(cue->endTime()).utf8().data(), toString(start).utf8().data(), toString(end).utf8().data()); cue->setStartTime(start); cue->setEndTime(end); cue->didChange(); }
void MediaSource::setDurationInternal(const MediaTime& duration) { // Duration Change Algorithm // https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#duration-change-algorithm // 1. If the current value of duration is equal to new duration, then return. if (duration == m_duration) return; // 2. Set old duration to the current value of duration. MediaTime oldDuration = m_duration; // 3. Update duration to new duration. m_duration = duration; // 4. If the new duration is less than old duration, then call remove(new duration, old duration) // on all objects in sourceBuffers. if (oldDuration.isValid() && duration < oldDuration) { for (auto& sourceBuffer : *m_sourceBuffers) sourceBuffer->rangeRemoval(duration, oldDuration); } // 5. If a user agent is unable to partially render audio frames or text cues that start before and end after the // duration, then run the following steps: // 5.1 Update new duration to the highest end time reported by the buffered attribute across all SourceBuffer objects // in sourceBuffers. // 5.2 Update duration to new duration. // NOTE: Assume UA is able to partially render audio frames. // 6. Update the media controller duration to new duration and run the HTMLMediaElement duration change algorithm. LOG(MediaSource, "MediaSource::setDurationInternal(%p) - duration(%g)", this, duration.toDouble()); m_private->durationChanged(); }
MediaTime abs(const MediaTime& rhs) { if (rhs.isInvalid()) return MediaTime::invalidTime(); if (rhs.isNegativeInfinite() || rhs.isPositiveInfinite()) return MediaTime::positiveInfiniteTime(); MediaTime val = rhs; val.m_timeValue *= signum(rhs.m_timeScale) * signum(rhs.m_timeValue); return val; }
MediaTime MediaPlayerPrivateAVFoundation::durationMediaTime() const { if (m_cachedDuration.isValid()) return m_cachedDuration; MediaTime duration = platformDuration(); if (!duration || duration.isInvalid()) return MediaTime::zeroTime(); m_cachedDuration = duration; LOG(Media, "MediaPlayerPrivateAVFoundation::duration(%p) - caching %s", this, toString(m_cachedDuration).utf8().data()); return m_cachedDuration; }
void InbandMetadataTextTrackPrivateAVF::addDataCue(const MediaTime& start, const MediaTime& end, PassRefPtr<SerializedPlatformRepresentation> prpCueData, const String& type) { ASSERT(cueFormat() == Data); if (!client()) return; RefPtr<SerializedPlatformRepresentation> cueData = prpCueData; m_currentCueStartTime = start; if (end.isPositiveInfinite()) m_incompleteCues.append(IncompleteMetaDataCue { cueData.get(), start }); client()->addDataCue(this, start, end, cueData, type); }
MediaTime MediaTime::operator-() const { if (isInvalid()) return invalidTime(); if (isIndefinite()) return indefiniteTime(); if (isPositiveInfinite()) return negativeInfiniteTime(); if (isNegativeInfinite()) return positiveInfiniteTime(); MediaTime negativeTime = *this; if (negativeTime.hasDoubleValue()) negativeTime.m_timeValueAsDouble = -negativeTime.m_timeValueAsDouble; else negativeTime.m_timeValue = -negativeTime.m_timeValue; return negativeTime; }
MediaTime MediaTime::operator*(int32_t rhs) const { if (isInvalid()) return invalidTime(); if (isIndefinite()) return indefiniteTime(); if (!rhs) return zeroTime(); if (isPositiveInfinite()) { if (rhs > 0) return positiveInfiniteTime(); return negativeInfiniteTime(); } if (isNegativeInfinite()) { if (rhs > 0) return negativeInfiniteTime(); return positiveInfiniteTime(); } MediaTime a = *this; if (a.hasDoubleValue()) { a.m_timeValueAsDouble *= rhs; return a; } while (!safeMultiply(a.m_timeValue, rhs, a.m_timeValue)) { if (a.m_timeScale == 1) return signum(a.m_timeValue) == signum(rhs) ? positiveInfiniteTime() : negativeInfiniteTime(); a.setTimeScale(a.m_timeScale / 2); } return a; }
MediaTime MediaTime::operator-(const MediaTime& rhs) const { if (rhs.isInvalid() || isInvalid()) return invalidTime(); if (rhs.isIndefinite() || isIndefinite()) return indefiniteTime(); if (isPositiveInfinite()) { if (rhs.isPositiveInfinite()) return invalidTime(); return positiveInfiniteTime(); } if (isNegativeInfinite()) { if (rhs.isNegativeInfinite()) return invalidTime(); return negativeInfiniteTime(); } int32_t commonTimeScale; if (!leastCommonMultiple(this->m_timeScale, rhs.m_timeScale, commonTimeScale) || commonTimeScale > MaximumTimeScale) commonTimeScale = MaximumTimeScale; MediaTime a = *this; MediaTime b = rhs; a.setTimeScale(commonTimeScale); b.setTimeScale(commonTimeScale); while (!safeSub(a.m_timeValue, b.m_timeValue, a.m_timeValue)) { if (commonTimeScale == 1) return a.m_timeValue > 0 ? positiveInfiniteTime() : negativeInfiniteTime(); commonTimeScale /= 2; a.setTimeScale(commonTimeScale); b.setTimeScale(commonTimeScale); } return a; }
MediaTime::ComparisonFlags MediaTime::compare(const MediaTime& rhs) const { if ((isPositiveInfinite() && rhs.isPositiveInfinite()) || (isNegativeInfinite() && rhs.isNegativeInfinite()) || (isInvalid() && rhs.isInvalid()) || (isIndefinite() && rhs.isIndefinite())) return EqualTo; if (isInvalid()) return GreaterThan; if (rhs.isInvalid()) return LessThan; if (rhs.isNegativeInfinite() || isPositiveInfinite()) return GreaterThan; if (rhs.isPositiveInfinite() || isNegativeInfinite()) return LessThan; if (isIndefinite()) return GreaterThan; if (rhs.isIndefinite()) return LessThan; int64_t rhsWhole = rhs.m_timeValue / rhs.m_timeScale; int64_t lhsWhole = m_timeValue / m_timeScale; if (lhsWhole > rhsWhole) return GreaterThan; if (lhsWhole < rhsWhole) return LessThan; int64_t rhsRemain = rhs.m_timeValue % rhs.m_timeScale; int64_t lhsRemain = m_timeValue % m_timeScale; int64_t lhsFactor = lhsRemain * rhs.m_timeScale; int64_t rhsFactor = rhsRemain * m_timeScale; if (lhsFactor == rhsFactor) return EqualTo; return lhsFactor > rhsFactor ? GreaterThan : LessThan; }
void InbandDataTextTrack::addDataCue(InbandTextTrackPrivate*, const MediaTime& start, const MediaTime& end, PassRefPtr<SerializedPlatformRepresentation> prpPlatformValue, const String& type) { RefPtr<SerializedPlatformRepresentation> platformValue = prpPlatformValue; if (m_incompleteCueMap.find(platformValue.get()) != m_incompleteCueMap.end()) return; auto cue = DataCue::create(*scriptExecutionContext(), start, end, platformValue.copyRef(), type); if (hasCue(cue.ptr(), TextTrackCue::IgnoreDuration)) { LOG(Media, "InbandDataTextTrack::addDataCue ignoring already added cue: start=%s, end=%s\n", toString(cue->startTime()).utf8().data(), toString(cue->endTime()).utf8().data()); return; } if (end.isPositiveInfinite() && mediaElement()) { cue->setEndTime(mediaElement()->durationMediaTime()); m_incompleteCueMap.add(WTFMove(platformValue), cue.copyRef()); } addCue(WTFMove(cue), ASSERT_NO_EXCEPTION); }
void MediaSource::seekToTime(const MediaTime& time) { // 2.4.3 Seeking // https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#mediasource-seeking m_pendingSeekTime = time; LOG(MediaSource, "MediaSource::seekToTime(%p, %f): %lu active source buffers", this, time.toDouble(), m_activeSourceBuffers->length()); // Run the following steps as part of the "Wait until the user agent has established whether or not the // media data for the new playback position is available, and, if it is, until it has decoded enough data // to play back that position" step of the seek algorithm: // 1. The media element looks for media segments containing the new playback position in each SourceBuffer // object in activeSourceBuffers. for (auto& sourceBuffer : *m_activeSourceBuffers) { // ↳ If one or more of the objects in activeSourceBuffers is missing media segments for the new // playback position if (!sourceBuffer->buffered()->ranges().contain(time)) { // 1.1 Set the HTMLMediaElement.readyState attribute to HAVE_METADATA. m_private->setReadyState(MediaPlayer::HaveMetadata); // 1.2 The media element waits until an appendBuffer() or an appendStream() call causes the coded // frame processing algorithm to set the HTMLMediaElement.readyState attribute to a value greater // than HAVE_METADATA. LOG(MediaSource, "MediaSource::seekToTime(%p) - waitForSeekCompleted()", this); m_private->waitForSeekCompleted(); return; } else LOG(MediaSource, "MediaSource::seekToTime(%p) - seek time buffered already.", this); // ↳ Otherwise // Continue } completeSeek(); }
MediaTime::ComparisonFlags MediaTime::compare(const MediaTime& rhs, bool fuzzy) const { if ((isPositiveInfinite() && rhs.isPositiveInfinite()) || (isNegativeInfinite() && rhs.isNegativeInfinite()) || (isInvalid() && rhs.isInvalid()) || (isIndefinite() && rhs.isIndefinite())) return EqualTo; if (isInvalid()) return GreaterThan; if (rhs.isInvalid()) return LessThan; if (rhs.isNegativeInfinite() || isPositiveInfinite()) return GreaterThan; if (rhs.isPositiveInfinite() || isNegativeInfinite()) return LessThan; if (isIndefinite()) return GreaterThan; if (rhs.isIndefinite()) return LessThan; if (hasDoubleValue() && rhs.hasDoubleValue()) { if (m_timeValueAsDouble == rhs.m_timeValueAsDouble) return EqualTo; if (fuzzy && fabs(m_timeValueAsDouble - rhs.m_timeValueAsDouble) <= fuzzinessThreshold().toDouble()) return EqualTo; return m_timeValueAsDouble < rhs.m_timeValueAsDouble ? LessThan : GreaterThan; } MediaTime a = *this; MediaTime b = rhs; if (a.hasDoubleValue()) a.setTimeScale(DefaultTimeScale); if (b.hasDoubleValue()) b.setTimeScale(DefaultTimeScale); int64_t rhsWhole = b.m_timeValue / b.m_timeScale; int64_t lhsWhole = a.m_timeValue / a.m_timeScale; if (lhsWhole > rhsWhole) return GreaterThan; if (lhsWhole < rhsWhole) return LessThan; int64_t rhsRemain = b.m_timeValue % b.m_timeScale; int64_t lhsRemain = a.m_timeValue % a.m_timeScale; int64_t lhsFactor = lhsRemain * b.m_timeScale; int64_t rhsFactor = rhsRemain * a.m_timeScale; if (lhsFactor == rhsFactor) return EqualTo; return lhsFactor > rhsFactor ? GreaterThan : LessThan; }
MediaTime MediaTime::operator-(const MediaTime& rhs) const { if (rhs.isInvalid() || isInvalid()) return invalidTime(); if (rhs.isIndefinite() || isIndefinite()) return indefiniteTime(); if (isPositiveInfinite() && rhs.isPositiveInfinite()) return invalidTime(); if (isNegativeInfinite() && rhs.isNegativeInfinite()) return invalidTime(); if (isPositiveInfinite() || rhs.isNegativeInfinite()) return positiveInfiniteTime(); if (isNegativeInfinite() || rhs.isPositiveInfinite()) return negativeInfiniteTime(); if (hasDoubleValue() && rhs.hasDoubleValue()) return MediaTime::createWithDouble(m_timeValueAsDouble - rhs.m_timeValueAsDouble); MediaTime a = *this; MediaTime b = rhs; if (a.hasDoubleValue()) a.setTimeScale(DefaultTimeScale); else if (b.hasDoubleValue()) b.setTimeScale(DefaultTimeScale); int32_t commonTimeScale; if (!leastCommonMultiple(this->m_timeScale, rhs.m_timeScale, commonTimeScale) || commonTimeScale > MaximumTimeScale) commonTimeScale = MaximumTimeScale; a.setTimeScale(commonTimeScale); b.setTimeScale(commonTimeScale); while (!safeSub(a.m_timeValue, b.m_timeValue, a.m_timeValue)) { if (commonTimeScale == 1) return a.m_timeValue > 0 ? positiveInfiniteTime() : negativeInfiniteTime(); commonTimeScale /= 2; a.setTimeScale(commonTimeScale); b.setTimeScale(commonTimeScale); } return a; }