Exemple #1
0
bool TextTrack::hasCue(VTTCue* cue, VTTCue::CueMatchRules match)
{
    if (cue->startTime() < 0 || cue->endTime() < 0)
        return false;
    
    if (!m_cues || !m_cues->length())
        return false;
    
    size_t searchStart = 0;
    size_t searchEnd = m_cues->length();
    
    while (1) {
        ASSERT(searchStart <= m_cues->length());
        ASSERT(searchEnd <= m_cues->length());
        
        TextTrackCue* existingCue;
        
        // Cues in the TextTrackCueList are maintained in start time order.
        if (searchStart == searchEnd) {
            if (!searchStart)
                return false;

            // If there is more than one cue with the same start time, back up to first one so we
            // consider all of them.
            while (searchStart >= 2 && cue->startTime() == m_cues->item(searchStart - 2)->startTime())
                --searchStart;
            
            bool firstCompare = true;
            while (1) {
                if (!firstCompare)
                    ++searchStart;
                firstCompare = false;
                if (searchStart > m_cues->length())
                    return false;

                existingCue = m_cues->item(searchStart - 1);
                if (!cue->isRenderable())
                    continue;

                if (!existingCue || cue->startTime() > existingCue->startTime())
                    return false;

                if (!toVTTCue(existingCue)->isEqual(*cue, match))
                    continue;
                
                return true;
            }
        }
        
        size_t index = (searchStart + searchEnd) / 2;
        existingCue = m_cues->item(index);
        if (cue->startTime() < existingCue->startTime() || (match != VTTCue::IgnoreDuration && cue->startTime() == existingCue->startTime() && cue->endTime() > existingCue->endTime()))
            searchEnd = index;
        else
            searchStart = index + 1;
    }
    
    ASSERT_NOT_REACHED();
    return false;
}
Exemple #2
0
void TextTrack::setMode(const AtomicString& mode)
{
    ASSERT(mode == disabledKeyword() || mode == hiddenKeyword() || mode == showingKeyword());

    // On setting, if the new value isn't equal to what the attribute would currently
    // return, the new value must be processed as follows ...
    if (m_mode == mode)
        return;

    // If mode changes to disabled, remove this track's cues from the client
    // because they will no longer be accessible from the cues() function.
    if (mode == disabledKeyword() && m_client && m_cues)
        m_client->textTrackRemoveCues(this, m_cues.get());
         
    if (mode != showingKeyword() && m_cues) {
        for (size_t i = 0; i < m_cues->length(); ++i) {
            TextTrackCue* cue = m_cues->item(i);
            if (cue->isRenderable())
                toVTTCue(cue)->removeDisplayTree();
        }
    }

    m_mode = mode;

    if (m_client)
        m_client->textTrackModeChanged(this);
}
Exemple #3
0
bool TextTrackCue::operator==(const TextTrackCue& cue) const
{
    if (cueType() != cue.cueType())
        return false;

    if (m_endTime != cue.endTime())
        return false;
    if (m_startTime != cue.startTime())
        return false;
    if (m_content != cue.text())
        return false;
    if (m_settings != cue.cueSettings())
        return false;
    if (m_id != cue.id())
        return false;
    if (m_textPosition != cue.position())
        return false;
    if (m_linePosition != cue.line())
        return false;
    if (m_cueSize != cue.size())
        return false;
    if (align() != cue.align())
        return false;
    
    return true;
}
Exemple #4
0
bool TextTrackCue::isEqual(const TextTrackCue& cue, CueMatchRules match) const
{
    if (cueType() != cue.cueType())
        return false;
    
    if (match != IgnoreDuration && m_endTime != cue.endTime())
        return false;
    if (m_startTime != cue.startTime())
        return false;
    if (m_content != cue.text())
        return false;
    if (m_settings != cue.cueSettings())
        return false;
    if (m_id != cue.id())
        return false;
    if (m_textPosition != cue.position())
        return false;
    if (m_linePosition != cue.line())
        return false;
    if (m_cueSize != cue.size())
        return false;
    if (align() != cue.align())
        return false;
    
    return true;
}
Exemple #5
0
void MediaControlRootElement::updateTextTrackDisplay()
{
    if (!m_textDisplayContainer)
        createTextTrackDisplay();

    CueList activeCues = toParentMediaElement(m_textDisplayContainer)->currentlyActiveCues();
    m_textTrackDisplay->removeChildren();
    bool nothingToDisplay = true;
    for (size_t i = 0; i < activeCues.size(); ++i) {
        TextTrackCue* cue = activeCues[i].data();
        ASSERT(cue->isActive());
        if (!cue->track() || cue->track()->mode() != TextTrack::SHOWING)
            continue;

        String cueText = cue->text();
        if (!cueText.isEmpty()) {
            if (!nothingToDisplay)
                m_textTrackDisplay->appendChild(document()->createElement(HTMLNames::brTag, false), ASSERT_NO_EXCEPTION);
            m_textTrackDisplay->appendChild(document()->createTextNode(cueText), ASSERT_NO_EXCEPTION);
            nothingToDisplay = false;
        }
    }

    if (!nothingToDisplay)
        m_textDisplayContainer->show();
    else
        m_textDisplayContainer->hide();
}
Exemple #6
0
void MediaControlTextTrackContainerElement::updateSizes(bool forceUpdate)
{
    HTMLMediaElement* mediaElement = toParentMediaElement(this);
    if (!mediaElement)
        return;

    if (!document()->page())
        return;

    IntRect videoBox;

    if (!mediaElement->renderer() || !mediaElement->renderer()->isVideo())
        return;
    videoBox = toRenderVideo(mediaElement->renderer())->videoBox();

    if (!forceUpdate && m_videoDisplaySize == videoBox)
        return;
    m_videoDisplaySize = videoBox;

    float smallestDimension = std::min(m_videoDisplaySize.size().height(), m_videoDisplaySize.size().width());

    float fontSize = smallestDimension * 0.05f;
    if (fontSize != m_fontSize) {
        m_fontSize = fontSize;
        setInlineStyleProperty(CSSPropertyFontSize, String::number(fontSize) + "px");
    }

    CueList activeCues = mediaElement->currentlyActiveCues();
    for (size_t i = 0; i < activeCues.size(); ++i) {
        TextTrackCue* cue = activeCues[i].data();
        cue->videoSizeDidChange(m_videoDisplaySize.size());
    }
}
void MediaControlTextTrackContainerElement::updateTimerFired(Timer<MediaControlTextTrackContainerElement>*)
{
    if (!document().page())
        return;

    if (m_textTrackRepresentation) {
        setInlineStyleProperty(CSSPropertyWidth, m_videoDisplaySize.size().width(), CSSPrimitiveValue::CSS_PX);
        setInlineStyleProperty(CSSPropertyHeight, m_videoDisplaySize.size().height(), CSSPrimitiveValue::CSS_PX);
    }
    
    HTMLMediaElement* mediaElement = parentMediaElement(this);
    if (!mediaElement)
        return;

    float smallestDimension = std::min(m_videoDisplaySize.size().height(), m_videoDisplaySize.size().width());
    float fontScale = document().page()->group().captionPreferences()->captionFontSizeScaleAndImportance(m_fontSizeIsImportant);
    m_fontSize = lroundf(smallestDimension * fontScale);
    
    CueList activeCues = mediaElement->currentlyActiveCues();
    for (size_t i = 0; i < activeCues.size(); ++i) {
        TextTrackCue* cue = activeCues[i].data();
        cue->setFontSize(m_fontSize, m_videoDisplaySize.size(), m_fontSizeIsImportant);
    }
    updateDisplay();
}
Exemple #8
0
bool TextTrackCue::cueContentsMatch(const TextTrackCue& cue) const
{
    if (cueType() != cue.cueType())
        return false;

    if (id() != cue.id())
        return false;

    return true;
}
Exemple #9
0
bool TextTrackCue::hasEquivalentStartTime(const TextTrackCue& cue) const
{
    MediaTime startTimeVariance = MediaTime::zeroTime();
    if (track())
        startTimeVariance = track()->startTimeVariance();
    else if (cue.track())
        startTimeVariance = cue.track()->startTimeVariance();

    return abs(abs(startMediaTime()) - abs(cue.startMediaTime())) <= startTimeVariance;
}
Exemple #10
0
bool TextTrackCue::isEqual(const TextTrackCue& cue, TextTrackCue::CueMatchRules match) const
{
    if (cueType() != cue.cueType())
        return false;

    if (match != IgnoreDuration && endMediaTime() != cue.endMediaTime())
        return false;
    if (!hasEquivalentStartTime(cue))
        return false;
    if (!cueContentsMatch(cue))
        return false;

    return true;
}
void JSTextTrackCue::visitChildren(JSCell* cell, SlotVisitor& visitor)
{
    JSTextTrackCue* jsTextTrackCue = jsCast<JSTextTrackCue*>(cell);
    ASSERT_GC_OBJECT_INHERITS(jsTextTrackCue, &s_info);
    COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag);
    ASSERT(jsTextTrackCue->structure()->typeInfo().overridesVisitChildren());
    Base::visitChildren(jsTextTrackCue, visitor);
    
    // Mark the cue's track root if it has one.
    TextTrackCue* textTrackCue = static_cast<TextTrackCue*>(jsTextTrackCue->impl());
    if (TextTrack* textTrack = textTrackCue->track())
        visitor.addOpaqueRoot(root(textTrack));
    
    textTrackCue->visitJSEventListeners(visitor);
}
Exemple #12
0
void
TextTrack::RemoveCue(TextTrackCue& aCue, ErrorResult& aRv)
{
  aCue.SetActive(false);

  mCueList->RemoveCue(aCue, aRv);
  aCue.SetTrack(nullptr);
  if (mTextTrackList) {
    HTMLMediaElement* mediaElement = mTextTrackList->GetMediaElement();
    if (mediaElement) {
      mediaElement->NotifyCueRemoved(aCue);
    }
  }
  SetDirty();
}
bool TextTrackCueGeneric::isEqual(const TextTrackCue& cue, TextTrackCue::CueMatchRules match) const
{
    // Do not call the parent class isEqual here, because we are not cueType() == VTTCue,
    // and will fail that equality test.
    if (!TextTrackCue::isEqual(cue, match))
        return false;

    if (cue.cueType() != TextTrackCue::Generic)
        return false;

    const TextTrackCueGeneric* other = static_cast<const TextTrackCueGeneric*>(&cue);

    if (m_baseFontSizeRelativeToVideoHeight != other->baseFontSizeRelativeToVideoHeight())
        return false;
    if (m_fontSizeMultiplier != other->fontSizeMultiplier())
        return false;
    if (m_fontName != other->fontName())
        return false;
    if (m_foregroundColor != other->foregroundColor())
        return false;
    if (m_backgroundColor != other->backgroundColor())
        return false;

    return true;
}
Exemple #14
0
bool DataCue::cueContentsMatch(const TextTrackCue& cue) const
{
    if (cue.cueType() != TextTrackCue::Data)
        return false;

    const DataCue* dataCue = toDataCue(&cue);
    RefPtr<ArrayBuffer> otherData = dataCue->data();
    if ((otherData && !m_data) || (!otherData && m_data))
        return false;
    if (m_data && m_data->byteLength() != otherData->byteLength())
        return false;
    if (m_data && m_data->data() && memcmp(m_data->data(), otherData->data(), m_data->byteLength()))
        return false;

    const SerializedPlatformRepresentation* otherPlatformValue = dataCue->platformValue();
    if ((otherPlatformValue && !m_platformValue) || (!otherPlatformValue && m_platformValue))
        return false;
    if (m_platformValue && !m_platformValue->isEqual(*otherPlatformValue))
        return false;

    JSC::JSValue thisValue = valueOrNull();
    JSC::JSValue otherValue = dataCue->valueOrNull();
    if ((otherValue && !thisValue) || (!otherValue && thisValue))
        return false;
    if (!JSC::JSValue::strictEqual(nullptr, thisValue, otherValue))
        return false;

    return true;
}
NS_IMETHODIMP
WebVTTListener::OnCue(JS::Handle<JS::Value> aCue, JSContext* aCx)
{
  if (!aCue.isObject()) {
    return NS_ERROR_FAILURE;
  }

  TextTrackCue* cue = nullptr;
  nsresult rv = UNWRAP_OBJECT(VTTCue, &aCue.toObject(), cue);
  NS_ENSURE_SUCCESS(rv, rv);

  cue->SetTrackElement(mElement);
  mElement->mTrack->AddCue(*cue);

  return NS_OK;
}
ExceptionOr<void> InbandDataTextTrack::removeCue(TextTrackCue& cue)
{
    ASSERT(cue.cueType() == TextTrackCue::Data);

    m_incompleteCueMap.remove(const_cast<SerializedPlatformRepresentation*>(toDataCue(&cue)->platformValue()));

    return InbandTextTrack::removeCue(cue);
}
Exemple #17
0
void
TextTrack::RemoveCue(TextTrackCue& aCue, ErrorResult& aRv)
{
  // Bug1304948, check the aCue belongs to the TextTrack.
  mCueList->RemoveCue(aCue, aRv);
  if (aRv.Failed()) {
    return;
  }
  aCue.SetActive(false);
  aCue.SetTrack(nullptr);
  if (mTextTrackList) {
    HTMLMediaElement* mediaElement = mTextTrackList->GetMediaElement();
    if (mediaElement) {
      mediaElement->NotifyCueRemoved(aCue);
    }
  }
  SetDirty();
}
Exemple #18
0
bool DataCue::isEqual(const TextTrackCue& cue, TextTrackCue::CueMatchRules match) const
{
    if (!TextTrackCue::isEqual(cue, match))
        return false;

    if (cue.cueType() != TextTrackCue::Data)
        return false;

    return cueContentsMatch(cue);
}
Exemple #19
0
bool TextTrackCue::doesExtendCue(const TextTrackCue& cue) const
{
    if (!cueContentsMatch(cue))
        return false;

    if (endMediaTime() != cue.startMediaTime())
        return false;

    return true;
}
bool JSTextTrackCueOwner::isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown> handle, void*, SlotVisitor& visitor)
{
    JSTextTrackCue* jsTextTrackCue = jsCast<JSTextTrackCue*>(handle.get().asCell());
    TextTrackCue* textTrackCue = static_cast<TextTrackCue*>(jsTextTrackCue->impl());

    // If the cue is firing event listeners, its wrapper is reachable because
    // the wrapper is responsible for marking those event listeners.
    if (textTrackCue->isFiringEventListeners())
        return true;

    // If the cue has no event listeners and has no custom properties, it is not reachable.
    if (!textTrackCue->hasEventListeners() && !jsTextTrackCue->hasCustomProperties())
        return false;

    // If the cue is not associated with a track, it is not reachable.
    if (!textTrackCue->track())
        return false;

    return visitor.containsOpaqueRoot(root(textTrackCue->track()));
}
bool TextTrackCue::isEqual(const TextTrackCue& cue, TextTrackCue::CueMatchRules match) const
{
    if (cueType() != cue.cueType())
        return false;

    if (match != IgnoreDuration && m_endTime != cue.m_endTime)
        return false;
    if (!cueContentsMatch(cue))
        return false;

    return true;
}
Exemple #22
0
bool TextTrackCueGeneric::isEqual(const TextTrackCue& cue, TextTrackCue::CueMatchRules match) const
{
    // Do not call the parent class isEqual here, because we are not cueType() == VTTCue,
    // and will fail that equality test.
    if (!TextTrackCue::isEqual(cue, match))
        return false;

    if (cue.cueType() != TextTrackCue::Generic)
        return false;

    return cueContentsMatch(cue);
}
void
TextTrackManager::NotifyCueRemoved(TextTrackCue& aCue)
{
  WEBVTT_LOG("NotifyCueRemoved");
  if (mNewCues) {
    mNewCues->RemoveCue(aCue);
  }
  DispatchTimeMarchesOn();
  if (aCue.GetActive()) {
    // We remove an active cue, need to update the display.
    DispatchUpdateCueDisplay();
  }
}
TextTrackCueList*
TextTrack::GetActiveCues()
{
  if (mMode == TextTrackMode::Disabled || !mMediaElement) {
    return nullptr;
  }

  // If we are dirty, i.e. an event happened that may cause the sorted mCueList
  // to have changed like a seek or an insert for a cue, than we need to rebuild
  // the active cue list from scratch.
  if (mDirty) {
    mCuePos = 0;
    mDirty = true;
    mActiveCueList->RemoveAll();
  }

  double playbackTime = mMediaElement->CurrentTime();
  // Remove all the cues from the active cue list whose end times now occur
  // earlier then the current playback time. When we reach a cue whose end time
  // is valid we can safely stop iterating as the list is sorted.
  for (uint32_t i = 0; i < mActiveCueList->Length() &&
                       (*mActiveCueList)[i]->EndTime() < playbackTime; i++) {
    mActiveCueList->RemoveCueAt(i);
  }
  // Add all the cues, starting from the position of the last cue that was
  // added, that have valid start and end times for the current playback time.
  // We can stop iterating safely once we encounter a cue that does not have
  // valid times for the current playback time as the cue list is sorted.
  for (; mCuePos < mCueList->Length(); mCuePos++) {
    TextTrackCue* cue = (*mCueList)[mCuePos];
    if (cue->StartTime() > playbackTime || cue->EndTime() < playbackTime) {
      break;
    }
    mActiveCueList->AddCue(*cue);
  }
  return mActiveCueList;
}
// https://html.spec.whatwg.org/multipage/embedded-content.html#time-marches-on
void
TextTrackManager::TimeMarchesOn()
{
  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  WEBVTT_LOG("TimeMarchesOn");
  mTimeMarchesOnDispatched = false;

  // Early return if we don't have any TextTracks or shutting down.
  if (!mTextTracks || mTextTracks->Length() == 0 || mShutdown) {
    return;
  }

  nsISupports* parentObject =
    mMediaElement->OwnerDoc()->GetParentObject();
  if (NS_WARN_IF(!parentObject)) {
    return;
  }
  nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(parentObject);

  if (mMediaElement &&
      (!(mMediaElement->GetPlayedOrSeeked()) || mMediaElement->Seeking())) {
    WEBVTT_LOG("TimeMarchesOn seeking or post return");
    return;
  }

  // Step 3.
  double currentPlaybackTime = mMediaElement->CurrentTime();
  bool hasNormalPlayback = !mHasSeeked;
  mHasSeeked = false;
  WEBVTT_LOG("TimeMarchesOn mLastTimeMarchesOnCalled %lf currentPlaybackTime %lf hasNormalPlayback %d"
      , mLastTimeMarchesOnCalled, currentPlaybackTime, hasNormalPlayback);

  // Step 1, 2.
  RefPtr<TextTrackCueList> currentCues =
    new TextTrackCueList(window);
  RefPtr<TextTrackCueList> otherCues =
    new TextTrackCueList(window);
  bool dummy;
  for (uint32_t index = 0; index < mTextTracks->Length(); ++index) {
    TextTrack* ttrack = mTextTracks->IndexedGetter(index, dummy);
    if (ttrack && dummy) {
      // TODO: call GetCueListByTimeInterval on mNewCues?
      ttrack->UpdateActiveCueList();
      TextTrackCueList* activeCueList = ttrack->GetActiveCues();
      if (activeCueList) {
        for (uint32_t i = 0; i < activeCueList->Length(); ++i) {
          currentCues->AddCue(*((*activeCueList)[i]));
        }
      }
    }
  }
  WEBVTT_LOGV("TimeMarchesOn currentCues %d", currentCues->Length());
  // Populate otherCues with 'non-active" cues.
  if (hasNormalPlayback) {
    if (currentPlaybackTime < mLastTimeMarchesOnCalled) {
      // TODO: Add log and find the root cause why the
      // playback position goes backward.
      mLastTimeMarchesOnCalled = currentPlaybackTime;
    }
    media::Interval<double> interval(mLastTimeMarchesOnCalled,
                                     currentPlaybackTime);
    otherCues = mNewCues->GetCueListByTimeInterval(interval);;
  } else {
    // Seek case. Put the mLastActiveCues into otherCues.
    otherCues = mLastActiveCues;
  }
  for (uint32_t i = 0; i < currentCues->Length(); ++i) {
    TextTrackCue* cue = (*currentCues)[i];
    otherCues->RemoveCue(*cue);
  }
  WEBVTT_LOGV("TimeMarchesOn otherCues %d", otherCues->Length());
  // Step 4.
  RefPtr<TextTrackCueList> missedCues = new TextTrackCueList(window);
  if (hasNormalPlayback) {
    for (uint32_t i = 0; i < otherCues->Length(); ++i) {
      TextTrackCue* cue = (*otherCues)[i];
      if (cue->StartTime() >= mLastTimeMarchesOnCalled &&
          cue->EndTime() <= currentPlaybackTime) {
        missedCues->AddCue(*cue);
      }
    }
  }
  WEBVTT_LOGV("TimeMarchesOn missedCues %d", missedCues->Length());
  // Step 5. Empty now.
  // TODO: Step 6: fire timeupdate?

  // Step 7. Abort steps if condition 1, 2, 3 are satisfied.
  // 1. All of the cues in current cues have their active flag set.
  // 2. None of the cues in other cues have their active flag set.
  // 3. Missed cues is empty.
  bool c1 = true;
  for (uint32_t i = 0; i < currentCues->Length(); ++i) {
    if (!(*currentCues)[i]->GetActive()) {
      c1 = false;
      break;
    }
  }
  bool c2 = true;
  for (uint32_t i = 0; i < otherCues->Length(); ++i) {
    if ((*otherCues)[i]->GetActive()) {
      c2 = false;
      break;
    }
  }
  bool c3 = (missedCues->Length() == 0);
  if (c1 && c2 && c3) {
    mLastTimeMarchesOnCalled = currentPlaybackTime;
    WEBVTT_LOG("TimeMarchesOn step 7 return, mLastTimeMarchesOnCalled %lf", mLastTimeMarchesOnCalled);
    return;
  }

  // Step 8. Respect PauseOnExit flag if not seek.
  if (hasNormalPlayback) {
    for (uint32_t i = 0; i < otherCues->Length(); ++i) {
      TextTrackCue* cue = (*otherCues)[i];
      if (cue && cue->PauseOnExit() && cue->GetActive()) {
        WEBVTT_LOG("TimeMarchesOn pause the MediaElement");
        mMediaElement->Pause();
        break;
      }
    }
    for (uint32_t i = 0; i < missedCues->Length(); ++i) {
      TextTrackCue* cue = (*missedCues)[i];
      if (cue && cue->PauseOnExit()) {
        WEBVTT_LOG("TimeMarchesOn pause the MediaElement");
        mMediaElement->Pause();
        break;
      }
    }
  }

  // Step 15.
  // Sort text tracks in the same order as the text tracks appear
  // in the media element's list of text tracks, and remove
  // duplicates.
  TextTrackListInternal affectedTracks;
  // Step 13, 14.
  nsTArray<RefPtr<SimpleTextTrackEvent>> eventList;
  // Step 9, 10.
  // For each text track cue in missed cues, prepare an event named
  // enter for the TextTrackCue object with the cue start time.
  for (uint32_t i = 0; i < missedCues->Length(); ++i) {
    TextTrackCue* cue = (*missedCues)[i];
    if (cue) {
      SimpleTextTrackEvent* event =
        new SimpleTextTrackEvent(NS_LITERAL_STRING("enter"),
                                 cue->StartTime(), cue->GetTrack(),
                                 cue);
      eventList.InsertElementSorted(event,
        CompareSimpleTextTrackEvents(mMediaElement));
      affectedTracks.AddTextTrack(cue->GetTrack(), CompareTextTracks(mMediaElement));
    }
  }

  // Step 11, 17.
  for (uint32_t i = 0; i < otherCues->Length(); ++i) {
    TextTrackCue* cue = (*otherCues)[i];
    if (cue->GetActive() || missedCues->IsCueExist(cue)) {
      double time = cue->StartTime() > cue->EndTime() ? cue->StartTime()
                                                      : cue->EndTime();
      SimpleTextTrackEvent* event =
        new SimpleTextTrackEvent(NS_LITERAL_STRING("exit"), time,
                                 cue->GetTrack(), cue);
      eventList.InsertElementSorted(event,
        CompareSimpleTextTrackEvents(mMediaElement));
      affectedTracks.AddTextTrack(cue->GetTrack(), CompareTextTracks(mMediaElement));
    }
    cue->SetActive(false);
  }

  // Step 12, 17.
  for (uint32_t i = 0; i < currentCues->Length(); ++i) {
    TextTrackCue* cue = (*currentCues)[i];
    if (!cue->GetActive()) {
      SimpleTextTrackEvent* event =
        new SimpleTextTrackEvent(NS_LITERAL_STRING("enter"),
                                 cue->StartTime(), cue->GetTrack(),
                                 cue);
      eventList.InsertElementSorted(event,
        CompareSimpleTextTrackEvents(mMediaElement));
      affectedTracks.AddTextTrack(cue->GetTrack(), CompareTextTracks(mMediaElement));
    }
    cue->SetActive(true);
  }

  // Fire the eventList
  for (uint32_t i = 0; i < eventList.Length(); ++i) {
    NS_DispatchToMainThread(eventList[i].forget());
  }

  // Step 16.
  for (uint32_t i = 0; i < affectedTracks.Length(); ++i) {
    TextTrack* ttrack = affectedTracks[i];
    if (ttrack) {
      ttrack->DispatchAsyncTrustedEvent(NS_LITERAL_STRING("cuechange"));
      HTMLTrackElement* trackElement = ttrack->GetTrackElement();
      if (trackElement) {
        trackElement->DispatchTrackRunnable(NS_LITERAL_STRING("cuechange"));
      }
    }
  }

  mLastTimeMarchesOnCalled = currentPlaybackTime;
  mLastActiveCues = currentCues;

  // Step 18.
  UpdateCueDisplay();
}
void MediaControlTextTrackContainerElement::updateDisplay()
{
    if (!mediaElement().closedCaptionsVisible()) {
        removeChildren();
        return;
    }

    // 1. If the media element is an audio element, or is another playback
    // mechanism with no rendering area, abort these steps. There is nothing to
    // render.
    if (isHTMLAudioElement(mediaElement()))
        return;

    // 2. Let video be the media element or other playback mechanism.
    HTMLVideoElement& video = toHTMLVideoElement(mediaElement());

    // 3. Let output be an empty list of absolutely positioned CSS block boxes.

    // 4. If the user agent is exposing a user interface for video, add to
    // output one or more completely transparent positioned CSS block boxes that
    // cover the same region as the user interface.

    // 5. If the last time these rules were run, the user agent was not exposing
    // a user interface for video, but now it is, let reset be true. Otherwise,
    // let reset be false.

    // There is nothing to be done explicitly for 4th and 5th steps, as
    // everything is handled through CSS. The caption box is on top of the
    // controls box, in a container set with the -webkit-box display property.

    // 6. Let tracks be the subset of video's list of text tracks that have as
    // their rules for updating the text track rendering these rules for
    // updating the display of WebVTT text tracks, and whose text track mode is
    // showing or showing by default.
    // 7. Let cues be an empty list of text track cues.
    // 8. For each track track in tracks, append to cues all the cues from
    // track's list of cues that have their text track cue active flag set.
    CueList activeCues = video.currentlyActiveCues();

    // 9. If reset is false, then, for each text track cue cue in cues: if cue's
    // text track cue display state has a set of CSS boxes, then add those boxes
    // to output, and remove cue from cues.

    // There is nothing explicitly to be done here, as all the caching occurs
    // within the TextTrackCue instance itself. If parameters of the cue change,
    // the display tree is cleared.

    // 10. For each text track cue cue in cues that has not yet had
    // corresponding CSS boxes added to output, in text track cue order, run the
    // following substeps:
    for (size_t i = 0; i < activeCues.size(); ++i) {
        TextTrackCue* cue = activeCues[i].data();

        ASSERT(cue->isActive());
        if (!cue->track() || !cue->track()->isRendered() || !cue->isActive())
            continue;

        cue->updateDisplay(m_videoDisplaySize.size(), *this);
    }

    // 11. Return output.
    if (hasChildren())
        show();
    else
        hide();
}
void MediaControlTextTrackContainerElement::updateDisplay()
{
    if (!mediaController()->closedCaptionsVisible())
        removeChildren();

    HTMLMediaElement* mediaElement = parentMediaElement(this);
    // 1. If the media element is an audio element, or is another playback
    // mechanism with no rendering area, abort these steps. There is nothing to
    // render.
    if (!mediaElement || !mediaElement->isVideo())
        return;

    // 2. Let video be the media element or other playback mechanism.
    HTMLVideoElement* video = toHTMLVideoElement(mediaElement);

    // 3. Let output be an empty list of absolutely positioned CSS block boxes.
    Vector<RefPtr<HTMLDivElement>> output;

    // 4. If the user agent is exposing a user interface for video, add to
    // output one or more completely transparent positioned CSS block boxes that
    // cover the same region as the user interface.

    // 5. If the last time these rules were run, the user agent was not exposing
    // a user interface for video, but now it is, let reset be true. Otherwise,
    // let reset be false.

    // There is nothing to be done explicitly for 4th and 5th steps, as
    // everything is handled through CSS. The caption box is on top of the
    // controls box, in a container set with the -webkit-box display property.

    // 6. Let tracks be the subset of video's list of text tracks that have as
    // their rules for updating the text track rendering these rules for
    // updating the display of WebVTT text tracks, and whose text track mode is
    // showing or showing by default.
    // 7. Let cues be an empty list of text track cues.
    // 8. For each track track in tracks, append to cues all the cues from
    // track's list of cues that have their text track cue active flag set.
    CueList activeCues = video->currentlyActiveCues();

    // 9. If reset is false, then, for each text track cue cue in cues: if cue's
    // text track cue display state has a set of CSS boxes, then add those boxes
    // to output, and remove cue from cues.

    // There is nothing explicitly to be done here, as all the caching occurs
    // within the TextTrackCue instance itself. If parameters of the cue change,
    // the display tree is cleared.

    // 10. For each text track cue cue in cues that has not yet had
    // corresponding CSS boxes added to output, in text track cue order, run the
    // following substeps:
    for (size_t i = 0; i < activeCues.size(); ++i) {
        TextTrackCue* cue = activeCues[i].data();

        ASSERT(cue->isActive());
        if (!cue->track() || !cue->track()->isRendered() || !cue->isActive() || cue->text().isEmpty())
            continue;

        RefPtr<TextTrackCueBox> displayBox = cue->getDisplayTree(m_videoDisplaySize.size());
        if (displayBox->hasChildNodes() && !contains(displayBox.get())) {
            // Note: the display tree of a cue is removed when the active flag of the cue is unset.
            appendChild(displayBox, ASSERT_NO_EXCEPTION, AttachNow);
            cue->setFontSize(m_fontSize, m_videoDisplaySize.size(), m_fontSizeIsImportant);
        }
    }

    // 11. Return output.
    if (hasChildNodes()) {
        show();
        if (mediaElement->requiresTextTrackRepresentation()) {
            if (!m_textTrackRepresentation)
                m_textTrackRepresentation = TextTrackRepresentation::create(this);
            mediaElement->setTextTrackRepresentation(m_textTrackRepresentation.get());

            if (Page* page = document().page())
                m_textTrackRepresentation->setContentScale(page->deviceScaleFactor());

            m_textTrackRepresentation->update();
            setInlineStyleProperty(CSSPropertyWidth, m_videoDisplaySize.size().width(), CSSPrimitiveValue::CSS_PX);
            setInlineStyleProperty(CSSPropertyHeight, m_videoDisplaySize.size().height(), CSSPrimitiveValue::CSS_PX);
        }
    } else {
        hide();
        clearTextTrackRepresentation();
    }
}
void MediaControlTextTrackContainerElement::updateDisplay()
{
    HTMLMediaElement* mediaElement = toParentMediaElement(this);

    // 1. If the media element is an audio element, or is another playback
    // mechanism with no rendering area, abort these steps. There is nothing to
    // render.
    if (!mediaElement->isVideo())
        return;

    // 2. Let video be the media element or other playback mechanism.
    HTMLVideoElement* video = static_cast<HTMLVideoElement*>(mediaElement);

    // 3. Let output be an empty list of absolutely positioned CSS block boxes.
    Vector<RefPtr<HTMLDivElement> > output;

    // 4. If the user agent is exposing a user interface for video, add to
    // output one or more completely transparent positioned CSS block boxes that
    // cover the same region as the user interface.

    // 5. If the last time these rules were run, the user agent was not exposing
    // a user interface for video, but now it is, let reset be true. Otherwise,
    // let reset be false.

    // There is nothing to be done explicitly for 4th and 5th steps, as
    // everything is handled through CSS. The caption box is on top of the
    // controls box, in a container set with the -webkit-box display property.

    // 6. Let tracks be the subset of video's list of text tracks that have as
    // their rules for updating the text track rendering these rules for
    // updating the display of WebVTT text tracks, and whose text track mode is
    // showing or showing by default.
    // 7. Let cues be an empty list of text track cues.
    // 8. For each track track in tracks, append to cues all the cues from
    // track's list of cues that have their text track cue active flag set.
    CueList activeCues = video->currentlyActiveCues();

    // 9. If reset is false, then, for each text track cue cue in cues: if cue's
    // text track cue display state has a set of CSS boxes, then add those boxes
    // to output, and remove cue from cues.

    // There is nothing explicitly to be done here, as all the caching occurs
    // within the TextTrackCue instance itself. If parameters of the cue change,
    // the display tree is cleared.

    // 10. For each text track cue cue in cues that has not yet had
    // corresponding CSS boxes added to output, in text track cue order, run the
    // following substeps:
    for (size_t i = 0; i < activeCues.size(); ++i) {
        TextTrackCue* cue = activeCues[i].data();

        ASSERT(cue->isActive());
        if (!cue->track() || !cue->track()->isRendered())
            continue;

        RefPtr<TextTrackCueBox> displayBox = cue->getDisplayTree();

        if (displayBox->hasChildNodes() && !contains(static_cast<Node*>(displayBox.get())))
            // Note: the display tree of a cue is removed when the active flag of the cue is unset.
            appendChild(displayBox, ASSERT_NO_EXCEPTION, false);
    }

    // 11. Return output.
    hasChildNodes() ? show() : hide();
}
Exemple #29
0
void CueTimeline::updateActiveCues(double movieTime) {
  // 4.8.10.8 Playing the media resource

  //  If the current playback position changes while the steps are running,
  //  then the user agent must wait for the steps to complete, and then must
  //  immediately rerun the steps.
  if (ignoreUpdateRequests())
    return;

  HTMLMediaElement& mediaElement = this->mediaElement();

  // Don't run the "time marches on" algorithm if the document has been
  // detached. This primarily guards against dispatch of events w/
  // HTMLTrackElement targets.
  if (mediaElement.document().isDetached())
    return;

  // https://html.spec.whatwg.org/#time-marches-on

  // 1 - Let current cues be a list of cues, initialized to contain all the
  // cues of all the hidden, showing, or showing by default text tracks of the
  // media element (not the disabled ones) whose start times are less than or
  // equal to the current playback position and whose end times are greater
  // than the current playback position.
  CueList currentCues;

  // The user agent must synchronously unset [the text track cue active] flag
  // whenever ... the media element's readyState is changed back to
  // kHaveNothing.
  if (mediaElement.getReadyState() != HTMLMediaElement::kHaveNothing &&
      mediaElement.webMediaPlayer())
    currentCues =
        m_cueTree.allOverlaps(m_cueTree.createInterval(movieTime, movieTime));

  CueList previousCues;
  CueList missedCues;

  // 2 - Let other cues be a list of cues, initialized to contain all the cues
  // of hidden, showing, and showing by default text tracks of the media
  // element that are not present in current cues.
  previousCues = m_currentlyActiveCues;

  // 3 - Let last time be the current playback position at the time this
  // algorithm was last run for this media element, if this is not the first
  // time it has run.
  double lastTime = m_lastUpdateTime;
  double lastSeekTime = mediaElement.lastSeekTime();

  // 4 - If the current playback position has, since the last time this
  // algorithm was run, only changed through its usual monotonic increase
  // during normal playback, then let missed cues be the list of cues in other
  // cues whose start times are greater than or equal to last time and whose
  // end times are less than or equal to the current playback position.
  // Otherwise, let missed cues be an empty list.
  if (lastTime >= 0 && lastSeekTime < movieTime) {
    CueList potentiallySkippedCues =
        m_cueTree.allOverlaps(m_cueTree.createInterval(lastTime, movieTime));

    for (CueInterval cue : potentiallySkippedCues) {
      // Consider cues that may have been missed since the last seek time.
      if (cue.low() > std::max(lastSeekTime, lastTime) &&
          cue.high() < movieTime)
        missedCues.append(cue);
    }
  }

  m_lastUpdateTime = movieTime;

  // 5 - If the time was reached through the usual monotonic increase of the
  // current playback position during normal playback, and if the user agent
  // has not fired a timeupdate event at the element in the past 15 to 250ms
  // and is not still running event handlers for such an event, then the user
  // agent must queue a task to fire a simple event named timeupdate at the
  // element. (In the other cases, such as explicit seeks, relevant events get
  // fired as part of the overall process of changing the current playback
  // position.)
  if (!mediaElement.seeking() && lastSeekTime < lastTime)
    mediaElement.scheduleTimeupdateEvent(true);

  // Explicitly cache vector sizes, as their content is constant from here.
  size_t missedCuesSize = missedCues.size();
  size_t previousCuesSize = previousCues.size();

  // 6 - If all of the cues in current cues have their text track cue active
  // flag set, none of the cues in other cues have their text track cue active
  // flag set, and missed cues is empty, then abort these steps.
  bool activeSetChanged = missedCuesSize;

  for (size_t i = 0; !activeSetChanged && i < previousCuesSize; ++i) {
    if (!currentCues.contains(previousCues[i]) &&
        previousCues[i].data()->isActive())
      activeSetChanged = true;
  }

  for (CueInterval currentCue : currentCues) {
    // Notify any cues that are already active of the current time to mark
    // past and future nodes. Any inactive cues have an empty display state;
    // they will be notified of the current time when the display state is
    // updated.
    if (currentCue.data()->isActive())
      currentCue.data()->updatePastAndFutureNodes(movieTime);
    else
      activeSetChanged = true;
  }

  if (!activeSetChanged)
    return;

  // 7 - If the time was reached through the usual monotonic increase of the
  // current playback position during normal playback, and there are cues in
  // other cues that have their text track cue pause-on-exi flag set and that
  // either have their text track cue active flag set or are also in missed
  // cues, then immediately pause the media element.
  for (size_t i = 0; !mediaElement.paused() && i < previousCuesSize; ++i) {
    if (previousCues[i].data()->pauseOnExit() &&
        previousCues[i].data()->isActive() &&
        !currentCues.contains(previousCues[i]))
      mediaElement.pause();
  }

  for (size_t i = 0; !mediaElement.paused() && i < missedCuesSize; ++i) {
    if (missedCues[i].data()->pauseOnExit())
      mediaElement.pause();
  }

  // 8 - Let events be a list of tasks, initially empty. Each task in this
  // list will be associated with a text track, a text track cue, and a time,
  // which are used to sort the list before the tasks are queued.
  HeapVector<std::pair<double, Member<TextTrackCue>>> eventTasks;

  // 8 - Let affected tracks be a list of text tracks, initially empty.
  HeapVector<Member<TextTrack>> affectedTracks;

  for (const auto& missedCue : missedCues) {
    // 9 - For each text track cue in missed cues, prepare an event named enter
    // for the TextTrackCue object with the text track cue start time.
    eventTasks.append(
        std::make_pair(missedCue.data()->startTime(), missedCue.data()));

    // 10 - For each text track [...] in missed cues, prepare an event
    // named exit for the TextTrackCue object with the  with the later of
    // the text track cue end time and the text track cue start time.

    // Note: An explicit task is added only if the cue is NOT a zero or
    // negative length cue. Otherwise, the need for an exit event is
    // checked when these tasks are actually queued below. This doesn't
    // affect sorting events before dispatch either, because the exit
    // event has the same time as the enter event.
    if (missedCue.data()->startTime() < missedCue.data()->endTime()) {
      eventTasks.append(
          std::make_pair(missedCue.data()->endTime(), missedCue.data()));
    }
  }

  for (const auto& previousCue : previousCues) {
    // 10 - For each text track cue in other cues that has its text
    // track cue active flag set prepare an event named exit for the
    // TextTrackCue object with the text track cue end time.
    if (!currentCues.contains(previousCue)) {
      eventTasks.append(
          std::make_pair(previousCue.data()->endTime(), previousCue.data()));
    }
  }

  for (const auto& currentCue : currentCues) {
    // 11 - For each text track cue in current cues that does not have its
    // text track cue active flag set, prepare an event named enter for the
    // TextTrackCue object with the text track cue start time.
    if (!previousCues.contains(currentCue)) {
      eventTasks.append(
          std::make_pair(currentCue.data()->startTime(), currentCue.data()));
    }
  }

  // 12 - Sort the tasks in events in ascending time order (tasks with earlier
  // times first).
  nonCopyingSort(eventTasks.begin(), eventTasks.end(), eventTimeCueCompare);

  for (const auto& task : eventTasks) {
    if (!affectedTracks.contains(task.second->track()))
      affectedTracks.append(task.second->track());

    // 13 - Queue each task in events, in list order.

    // Each event in eventTasks may be either an enterEvent or an exitEvent,
    // depending on the time that is associated with the event. This
    // correctly identifies the type of the event, if the startTime is
    // less than the endTime in the cue.
    if (task.second->startTime() >= task.second->endTime()) {
      mediaElement.scheduleEvent(
          createEventWithTarget(EventTypeNames::enter, task.second.get()));
      mediaElement.scheduleEvent(
          createEventWithTarget(EventTypeNames::exit, task.second.get()));
    } else {
      bool isEnterEvent = task.first == task.second->startTime();
      AtomicString eventName =
          isEnterEvent ? EventTypeNames::enter : EventTypeNames::exit;
      mediaElement.scheduleEvent(
          createEventWithTarget(eventName, task.second.get()));
    }
  }

  // 14 - Sort affected tracks in the same order as the text tracks appear in
  // the media element's list of text tracks, and remove duplicates.
  nonCopyingSort(affectedTracks.begin(), affectedTracks.end(),
                 trackIndexCompare);

  // 15 - For each text track in affected tracks, in the list order, queue a
  // task to fire a simple event named cuechange at the TextTrack object, and,
  // ...
  for (const auto& track : affectedTracks) {
    mediaElement.scheduleEvent(
        createEventWithTarget(EventTypeNames::cuechange, track.get()));

    // ... if the text track has a corresponding track element, to then fire a
    // simple event named cuechange at the track element as well.
    if (track->trackType() == TextTrack::TrackElement) {
      HTMLTrackElement* trackElement =
          static_cast<LoadableTextTrack*>(track.get())->trackElement();
      DCHECK(trackElement);
      mediaElement.scheduleEvent(
          createEventWithTarget(EventTypeNames::cuechange, trackElement));
    }
  }

  // 16 - Set the text track cue active flag of all the cues in the current
  // cues, and unset the text track cue active flag of all the cues in the
  // other cues.
  for (const auto& cue : currentCues)
    cue.data()->setIsActive(true);

  for (const auto& previousCue : previousCues) {
    if (!currentCues.contains(previousCue)) {
      TextTrackCue* cue = previousCue.data();
      cue->setIsActive(false);
      cue->removeDisplayTree();
    }
  }

  // Update the current active cues.
  m_currentlyActiveCues = currentCues;
  mediaElement.updateTextTrackDisplay();
}
Exemple #30
0
bool TextTrack::hasCue(TextTrackCue* cue, TextTrackCue::CueMatchRules match)
{
    if (cue->startTime() < 0 || cue->endTime() < 0)
        return false;
    
    if (!m_cues || !m_cues->length())
        return false;
    
    size_t searchStart = 0;
    size_t searchEnd = m_cues->length();
    
    while (1) {
        ASSERT(searchStart <= m_cues->length());
        ASSERT(searchEnd <= m_cues->length());
        
        TextTrackCue* existingCue;
        
        // Cues in the TextTrackCueList are maintained in start time order.
        if (searchStart == searchEnd) {
            if (!searchStart)
                return false;

            // If there is more than one cue with the same start time, back up to first one so we
            // consider all of them.
            while (searchStart >= 2 && cue->hasEquivalentStartTime(*m_cues->item(searchStart - 2)))
                --searchStart;
            
            bool firstCompare = true;
            while (1) {
                if (!firstCompare)
                    ++searchStart;
                firstCompare = false;
                if (searchStart > m_cues->length())
                    return false;

                existingCue = m_cues->item(searchStart - 1);
                if (!existingCue)
                    return false;

                if (existingCue->doesExtendCue(*cue)) {
                    existingCue->setEndTime(cue->endTime(), IGNORE_EXCEPTION);
                    return true;
                }

                if (cue->startTime() > (existingCue->startTime() + startTimeVariance()))
                    return false;

                if (existingCue->isEqual(*cue, match))
                    return true;
            }
        }
        
        size_t index = (searchStart + searchEnd) / 2;
        existingCue = m_cues->item(index);
        if ((cue->startTime() + startTimeVariance()) < existingCue->startTime() || (match != TextTrackCue::IgnoreDuration && cue->hasEquivalentStartTime(*existingCue) && cue->endTime() > existingCue->endTime()))
            searchEnd = index;
        else
            searchStart = index + 1;
    }
    
    ASSERT_NOT_REACHED();
    return false;
}