Ejemplo n.º 1
0
already_AddRefed<TextTrack>
TextTrackManager::AddTextTrack(TextTrackKind aKind, const nsAString& aLabel,
                               const nsAString& aLanguage,
                               TextTrackMode aMode,
                               TextTrackReadyState aReadyState,
                               TextTrackSource aTextTrackSource)
{
  if (!mMediaElement || !mTextTracks) {
    return nullptr;
  }
  WEBVTT_LOG("%p AddTextTrack",this);
  WEBVTT_LOGV("AddTextTrack kind %d Label %s Language %s",aKind,
    NS_ConvertUTF16toUTF8(aLabel).get(), NS_ConvertUTF16toUTF8(aLanguage).get());
  RefPtr<TextTrack> track =
    mTextTracks->AddTextTrack(aKind, aLabel, aLanguage, aMode, aReadyState,
                              aTextTrackSource, CompareTextTracks(mMediaElement));
  AddCues(track);
  ReportTelemetryForTrack(track);

  if (aTextTrackSource == TextTrackSource::Track) {
    NS_DispatchToMainThread(NewRunnableMethod(this, &TextTrackManager::HonorUserPreferencesForTrackSelection));
  }

  return track.forget();
}
Ejemplo n.º 2
0
TextTrackManager::TextTrackManager(HTMLMediaElement *aMediaElement)
  : mMediaElement(aMediaElement)
  , mHasSeeked(false)
  , mLastTimeMarchesOnCalled(0.0)
  , mTimeMarchesOnDispatched(false)
  , mUpdateCueDisplayDispatched(false)
  , performedTrackSelection(false)
  , mCueTelemetryReported(false)
  , mShutdown(false)
{
  nsISupports* parentObject =
    mMediaElement->OwnerDoc()->GetParentObject();

  NS_ENSURE_TRUE_VOID(parentObject);
  WEBVTT_LOG("%p Create TextTrackManager",this);
  nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(parentObject);
  mNewCues = new TextTrackCueList(window);
  mLastActiveCues = new TextTrackCueList(window);
  mTextTracks = new TextTrackList(window, this);
  mPendingTextTracks = new TextTrackList(window, this);

  if (!sParserWrapper) {
    nsCOMPtr<nsIWebVTTParserWrapper> parserWrapper =
      do_CreateInstance(NS_WEBVTTPARSERWRAPPER_CONTRACTID);
    sParserWrapper = parserWrapper;
    ClearOnShutdown(&sParserWrapper);
  }
  mShutdownProxy = new ShutdownObserverProxy(this);
}
Ejemplo n.º 3
0
already_AddRefed<TextTrack> TextTrackManager::AddTextTrack(
    TextTrackKind aKind, const nsAString& aLabel, const nsAString& aLanguage,
    TextTrackMode aMode, TextTrackReadyState aReadyState,
    TextTrackSource aTextTrackSource) {
  if (!mMediaElement || !mTextTracks) {
    return nullptr;
  }
  RefPtr<TextTrack> track = mTextTracks->AddTextTrack(
      aKind, aLabel, aLanguage, aMode, aReadyState, aTextTrackSource,
      CompareTextTracks(mMediaElement));
  WEBVTT_LOG("AddTextTrack %p kind %" PRIu32 " Label %s Language %s",
             track.get(), static_cast<uint32_t>(aKind), NS_ConvertUTF16toUTF8(aLabel).get(),
             NS_ConvertUTF16toUTF8(aLanguage).get());
  AddCues(track);
  ReportTelemetryForTrack(track);

  if (aTextTrackSource == TextTrackSource::Track) {
    RefPtr<nsIRunnable> task = NewRunnableMethod(
        "dom::TextTrackManager::HonorUserPreferencesForTrackSelection", this,
        &TextTrackManager::HonorUserPreferencesForTrackSelection);
    nsContentUtils::RunInStableState(task.forget());
  }

  return track.forget();
}
Ejemplo n.º 4
0
void
TextTrackManager::HonorUserPreferencesForTrackSelection()
{
  if (performedTrackSelection || !mTextTracks) {
    return;
  }
  WEBVTT_LOG("HonorUserPreferencesForTrackSelection");
  TextTrackKind ttKinds[] = { TextTrackKind::Captions,
                              TextTrackKind::Subtitles };

  // Steps 1 - 3: Perform automatic track selection for different TextTrack
  // Kinds.
  PerformTrackSelection(ttKinds, ArrayLength(ttKinds));
  PerformTrackSelection(TextTrackKind::Descriptions);
  PerformTrackSelection(TextTrackKind::Chapters);

  // Step 4: Set all TextTracks with a kind of metadata that are disabled
  // to hidden.
  for (uint32_t i = 0; i < mTextTracks->Length(); i++) {
    TextTrack* track = (*mTextTracks)[i];
    if (track->Kind() == TextTrackKind::Metadata && TrackIsDefault(track) &&
        track->Mode() == TextTrackMode::Disabled) {
      track->SetMode(TextTrackMode::Hidden);
    }
  }

  performedTrackSelection = true;
}
Ejemplo n.º 5
0
void
TextTrackManager::NotifyCueUpdated(TextTrackCue *aCue)
{
  // TODO: Add/Reorder the cue to mNewCues if we have some optimization?
  WEBVTT_LOG("NotifyCueUpdated");
  DispatchTimeMarchesOn();
}
Ejemplo n.º 6
0
void TextTrackManager::NotifyCueRemoved(TextTrackCue& aCue) {
  WEBVTT_LOG("NotifyCueRemoved, cue=%p", &aCue);
  if (mNewCues) {
    mNewCues->RemoveCue(aCue);
  }
  TimeMarchesOn();
  DispatchUpdateCueDisplay();
}
Ejemplo n.º 7
0
void TextTrackManager::NotifyCueAdded(TextTrackCue& aCue) {
  WEBVTT_LOG("NotifyCueAdded, cue=%p", &aCue);
  if (mNewCues) {
    mNewCues->AddCue(aCue);
  }
  TimeMarchesOn();
  ReportTelemetryForCue();
}
Ejemplo n.º 8
0
void TextTrackManager::NotifyCueUpdated(TextTrackCue* aCue) {
  // TODO: Add/Reorder the cue to mNewCues if we have some optimization?
  WEBVTT_LOG("NotifyCueUpdated, cue=%p", aCue);
  TimeMarchesOn();
  // For the case "Texttrack.mode = hidden/showing", if the mode
  // changing between showing and hidden, TimeMarchesOn
  // doesn't render the cue. Call DispatchUpdateCueDisplay() explicitly.
  DispatchUpdateCueDisplay();
}
Ejemplo n.º 9
0
void TextTrackManager::DidSeek() {
  WEBVTT_LOG("DidSeek");
  if (mMediaElement) {
    mLastTimeMarchesOnCalled = mMediaElement->CurrentTime();
    WEBVTT_LOGV("DidSeek set mLastTimeMarchesOnCalled %lf",
                mLastTimeMarchesOnCalled);
  }
  mHasSeeked = true;
}
Ejemplo n.º 10
0
void
TextTrackManager::UpdateCueDisplay()
{
  WEBVTT_LOG("UpdateCueDisplay");
  mUpdateCueDisplayDispatched = false;

  if (!mMediaElement || !mTextTracks) {
    return;
  }

  nsIFrame* frame = mMediaElement->GetPrimaryFrame();
  nsVideoFrame* videoFrame = do_QueryFrame(frame);
  if (!videoFrame) {
    return;
  }

  nsCOMPtr<nsIContent> overlay = videoFrame->GetCaptionOverlay();
  nsCOMPtr<nsIContent> controls = videoFrame->GetVideoControls();
  if (!overlay) {
    return;
  }

  nsTArray<RefPtr<TextTrackCue> > activeCues;
  mTextTracks->GetShowingCues(activeCues);

  if (activeCues.Length() > 0) {
    WEBVTT_LOG("UpdateCueDisplay ProcessCues");
    WEBVTT_LOGV("UpdateCueDisplay activeCues.Length() %d",activeCues.Length());
    RefPtr<nsVariantCC> jsCues = new nsVariantCC();

    jsCues->SetAsArray(nsIDataType::VTYPE_INTERFACE,
                       &NS_GET_IID(nsIDOMEventTarget),
                       activeCues.Length(),
                       static_cast<void*>(activeCues.Elements()));
    nsPIDOMWindowInner* window = mMediaElement->OwnerDoc()->GetInnerWindow();
    if (window) {
      sParserWrapper->ProcessCues(window, jsCues, overlay, controls);
    }
  } else if (overlay->Length() > 0) {
    WEBVTT_LOG("UpdateCueDisplay EmptyString");
    nsContentUtils::SetNodeTextContent(overlay, EmptyString(), true);
  }
}
Ejemplo n.º 11
0
void
TextTrackManager::DispatchUpdateCueDisplay()
{
  if (!mUpdateCueDisplayDispatched && !mShutdown &&
      (mMediaElement->GetHasUserInteraction() || mMediaElement->IsCurrentlyPlaying())) {
    WEBVTT_LOG("DispatchUpdateCueDisplay");
    NS_DispatchToMainThread(NewRunnableMethod(this, &TextTrackManager::UpdateCueDisplay));
    mUpdateCueDisplayDispatched = true;
  }
}
Ejemplo n.º 12
0
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();
  }
}
Ejemplo n.º 13
0
void
TextTrackManager::DispatchTimeMarchesOn()
{
  // Run the algorithm if no previous instance is still running, otherwise
  // enqueue the current playback position and whether only that changed
  // through its usual monotonic increase during normal playback; current
  // executing call upon completion will check queue for further 'work'.
  if (!mTimeMarchesOnDispatched && !mShutdown &&
      (mMediaElement->GetHasUserInteraction() || mMediaElement->IsCurrentlyPlaying())) {
    WEBVTT_LOG("DispatchTimeMarchesOn");
    NS_DispatchToMainThread(NewRunnableMethod(this, &TextTrackManager::TimeMarchesOn));
    mTimeMarchesOnDispatched = true;
  }
}
Ejemplo n.º 14
0
void TextTrackManager::DispatchUpdateCueDisplay() {
  if (!mUpdateCueDisplayDispatched && !IsShutdown() &&
      mMediaElement->IsCurrentlyPlaying()) {
    WEBVTT_LOG("DispatchUpdateCueDisplay");
    nsPIDOMWindowInner* win = mMediaElement->OwnerDoc()->GetInnerWindow();
    if (win) {
      nsGlobalWindowInner::Cast(win)->Dispatch(
          TaskCategory::Other,
          NewRunnableMethod("dom::TextTrackManager::UpdateCueDisplay", this,
                            &TextTrackManager::UpdateCueDisplay));
      mUpdateCueDisplayDispatched = true;
    }
  }
}
Ejemplo n.º 15
0
void
TextTrackManager::AddTextTrack(TextTrack* aTextTrack)
{
  if (!mMediaElement || !mTextTracks) {
    return;
  }
  WEBVTT_LOG("%p AddTextTrack TextTrack %p",this, aTextTrack);
  mTextTracks->AddTextTrack(aTextTrack, CompareTextTracks(mMediaElement));
  AddCues(aTextTrack);
  ReportTelemetryForTrack(aTextTrack);

  if (aTextTrack->GetTextTrackSource() == TextTrackSource::Track) {
    NS_DispatchToMainThread(NewRunnableMethod(this, &TextTrackManager::HonorUserPreferencesForTrackSelection));
  }
}
Ejemplo n.º 16
0
void TextTrackManager::AddCues(TextTrack* aTextTrack) {
  if (!mNewCues) {
    WEBVTT_LOG("AddCues mNewCues is null");
    return;
  }

  TextTrackCueList* cueList = aTextTrack->GetCues();
  if (cueList) {
    bool dummy;
    WEBVTT_LOGV("AddCues, CuesNum=%d", cueList->Length());
    for (uint32_t i = 0; i < cueList->Length(); ++i) {
      mNewCues->AddCue(*cueList->IndexedGetter(i, dummy));
    }
    TimeMarchesOn();
  }
}
Ejemplo n.º 17
0
void TextTrackManager::AddTextTrack(TextTrack* aTextTrack) {
  if (!mMediaElement || !mTextTracks) {
    return;
  }
  WEBVTT_LOG("AddTextTrack TextTrack %p", aTextTrack);
  mTextTracks->AddTextTrack(aTextTrack, CompareTextTracks(mMediaElement));
  AddCues(aTextTrack);
  ReportTelemetryForTrack(aTextTrack);

  if (aTextTrack->GetTextTrackSource() == TextTrackSource::Track) {
    RefPtr<nsIRunnable> task = NewRunnableMethod(
        "dom::TextTrackManager::HonorUserPreferencesForTrackSelection", this,
        &TextTrackManager::HonorUserPreferencesForTrackSelection);
    nsContentUtils::RunInStableState(task.forget());
  }
}
Ejemplo n.º 18
0
void TextTrackManager::DispatchTimeMarchesOn() {
  // Run the algorithm if no previous instance is still running, otherwise
  // enqueue the current playback position and whether only that changed
  // through its usual monotonic increase during normal playback; current
  // executing call upon completion will check queue for further 'work'.
  if (!mTimeMarchesOnDispatched && !IsShutdown() &&
      mMediaElement->IsCurrentlyPlaying()) {
    WEBVTT_LOG("DispatchTimeMarchesOn");
    nsPIDOMWindowInner* win = mMediaElement->OwnerDoc()->GetInnerWindow();
    if (win) {
      nsGlobalWindowInner::Cast(win)->Dispatch(
          TaskCategory::Other,
          NewRunnableMethod("dom::TextTrackManager::TimeMarchesOn", this,
                            &TextTrackManager::TimeMarchesOn));
      mTimeMarchesOnDispatched = true;
    }
  }
}
Ejemplo n.º 19
0
void
TextTrackManager::RemoveTextTrack(TextTrack* aTextTrack, bool aPendingListOnly)
{
  if (!mPendingTextTracks || !mTextTracks) {
    return;
  }

  WEBVTT_LOG("%p RemoveTextTrack TextTrack %p", this, aTextTrack);
  mPendingTextTracks->RemoveTextTrack(aTextTrack);
  if (aPendingListOnly) {
    return;
  }

  mTextTracks->RemoveTextTrack(aTextTrack);
  // Remove the cues in mNewCues belong to aTextTrack.
  TextTrackCueList* removeCueList = aTextTrack->GetCues();
  if (removeCueList) {
    WEBVTT_LOGV("RemoveTextTrack removeCueList->Length() %d", removeCueList->Length());
    for (uint32_t i = 0; i < removeCueList->Length(); ++i) {
      mNewCues->RemoveCue(*((*removeCueList)[i]));
    }
    DispatchTimeMarchesOn();
  }
}
Ejemplo n.º 20
0
TextTrackManager::~TextTrackManager() {
  WEBVTT_LOG("~TextTrackManager");
  mShutdownProxy->Unregister();
}
Ejemplo n.º 21
0
void
TextTrackManager::NotifyReset()
{
  WEBVTT_LOG("NotifyReset");
  mLastTimeMarchesOnCalled = 0.0;
}
Ejemplo n.º 22
0
// 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();
}
Ejemplo n.º 23
0
TextTrackManager::~TextTrackManager()
{
  WEBVTT_LOG("%p ~TextTrackManager",this);
  nsContentUtils::UnregisterShutdownObserver(mShutdownProxy);
}