void TextTrack::addCue(PassRefPtr<TextTrackCue> prpCue) { if (!prpCue) return; RefPtr<TextTrackCue> cue = prpCue; // TODO(93143): Add spec-compliant behavior for negative time values. if (std::isnan(cue->startTime()) || std::isnan(cue->endTime()) || cue->startTime() < 0 || cue->endTime() < 0) return; // 4.8.10.12.5 Text track API // The addCue(cue) method of TextTrack objects, when invoked, must run the following steps: // 1. If the given cue is in a text track list of cues, then remove cue from that text track // list of cues. TextTrack* cueTrack = cue->track(); if (cueTrack && cueTrack != this) cueTrack->removeCue(cue.get(), ASSERT_NO_EXCEPTION); // 2. Add cue to the method's TextTrack object's text track's text track list of cues. cue->setTrack(this); ensureTextTrackCueList()->add(cue); if (m_client) m_client->textTrackAddCue(this, cue.get()); }
static void oncuechangeAttrSetter(v8::Local<v8::String> name, v8::Local<v8::Value> value, const v8::AccessorInfo& info) { TextTrack* imp = V8TextTrack::toNative(info.Holder()); transferHiddenDependency(info.Holder(), imp->oncuechange(), value, V8TextTrack::eventListenerCacheIndex); imp->setOncuechange(V8DOMWrapper::getEventListener(value, true, ListenerFindOrCreate)); return; }
static void modeAttrSetter(v8::Local<v8::String> name, v8::Local<v8::Value> value, const v8::AccessorInfo& info) { TextTrack* imp = V8TextTrack::toNative(info.Holder()); V8TRYCATCH_FOR_V8STRINGRESOURCE_VOID(V8StringResource<>, v, value); imp->setMode(v); return; }
static int textTrackSelectionScore(const TextTrack& track) { if (track.kind() != TextTrack::captionsKeyword() && track.kind() != TextTrack::subtitlesKeyword()) return 0; return textTrackLanguageSelectionScore(track); }
void TextTrack::addRegion(PassRefPtr<VTTRegion> prpRegion) { if (!prpRegion) return; RefPtr<VTTRegion> region = prpRegion; VTTRegionList* regionList = ensureVTTRegionList(); // 1. If the given region is in a text track list of regions, then remove // region from that text track list of regions. TextTrack* regionTrack = region->track(); if (regionTrack && regionTrack != this) regionTrack->removeRegion(region.get(), ASSERT_NO_EXCEPTION); // 2. If the method's TextTrack object's text track list of regions contains // a region with the same identifier as region replace the values of that // region's width, height, anchor point, viewport anchor point and scroll // attributes with those of region. VTTRegion* existingRegion = regionList->getRegionById(region->id()); if (existingRegion) { existingRegion->updateParametersFromRegion(region.get()); return; } // Otherwise: add region to the method's TextTrack object's text track // list of regions. region->setTrack(this); regionList->add(region); }
void TextTrackManager::HonorUserPreferencesForTrackSelection() { if (performedTrackSelection) { return; } 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; }
void MediaControls::showTextTrackAtIndex(unsigned indexToEnable) { TextTrackList* trackList = mediaElement().textTracks(); if (indexToEnable >= trackList->length()) return; TextTrack* track = trackList->anonymousIndexedGetter(indexToEnable); if (track && track->canBeRendered()) track->setMode(TextTrack::showingKeyword()); }
void MediaControls::disableShowingTextTracks() { TextTrackList* trackList = mediaElement().textTracks(); for (unsigned i = 0; i < trackList->length(); ++i) { TextTrack* track = trackList->anonymousIndexedGetter(i); if (track->mode() == TextTrack::showingKeyword()) track->setMode(TextTrack::disabledKeyword()); } }
static v8::Handle<v8::Value> addCueCallback(const v8::Arguments& args) { if (args.Length() < 1) return throwNotEnoughArgumentsError(args.GetIsolate()); TextTrack* imp = V8TextTrack::toNative(args.Holder()); V8TRYCATCH(TextTrackCue*, cue, V8TextTrackCue::HasInstance(MAYBE_MISSING_PARAMETER(args, 0, DefaultIsUndefined)) ? V8TextTrackCue::toNative(v8::Handle<v8::Object>::Cast(MAYBE_MISSING_PARAMETER(args, 0, DefaultIsUndefined))) : 0); imp->addCue(cue); return v8Undefined(); }
void TextTrackManager::GetTextTracksOfKind(TextTrackKind aTextTrackKind, nsTArray<TextTrack*>& aTextTracks) { for (uint32_t i = 0; i < mTextTracks->Length(); i++) { TextTrack* textTrack = (*mTextTracks)[i]; if (textTrack->Kind() == aTextTrackKind) { aTextTracks.AppendElement(textTrack); } } }
static v8::Handle<v8::Value> activeCuesAttrGetter(v8::Local<v8::String> name, const v8::AccessorInfo& info) { TextTrack* imp = V8TextTrack::toNative(info.Holder()); RefPtr<TextTrackCueList> result = imp->activeCues(); v8::Handle<v8::Value> wrapper = result.get() ? v8::Handle<v8::Value>(DOMDataStore::getWrapper(result.get(), info.GetIsolate())) : v8Undefined(); if (wrapper.IsEmpty()) { wrapper = toV8(result.get(), info.Holder(), info.GetIsolate()); if (!wrapper.IsEmpty()) V8DOMWrapper::setNamedHiddenReference(info.Holder(), "activeCues", wrapper); } return wrapper; }
static int textTrackLanguageSelectionScore(const TextTrack& track) { if (track.language().isEmpty()) return 0; Vector<AtomicString> languages = userPreferredLanguages(); size_t languageMatchIndex = indexOfBestMatchingLanguageInList(track.language(), languages); if (languageMatchIndex >= languages.size()) return 0; return languages.size() - languageMatchIndex; }
void TextTrackManager::PopulatePendingList() { uint32_t len = mTextTracks->Length(); bool dummy; for (uint32_t index = 0; index < len; ++index) { TextTrack* ttrack = mTextTracks->IndexedGetter(index, dummy); if (ttrack && ttrack->Mode() != TextTrackMode::Disabled && ttrack->ReadyState() == HTMLTrackElement::LOADING) { mPendingTextTracks->AddTextTrack(ttrack); } } }
void JSTextTrack::visitChildren(JSCell* cell, SlotVisitor& visitor) { JSTextTrack* jsTextTrack = jsCast<JSTextTrack*>(cell); ASSERT_GC_OBJECT_INHERITS(jsTextTrack, &s_info); COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag); ASSERT(jsTextTrack->structure()->typeInfo().overridesVisitChildren()); Base::visitChildren(jsTextTrack, visitor); TextTrack* textTrack = static_cast<TextTrack*>(jsTextTrack->impl()); visitor.addOpaqueRoot(root(textTrack)); textTrack->visitJSEventListeners(visitor); }
TextTrack* TextTrackList::getTrackById(const AtomicString& id) { // 4.8.10.12.5 Text track API // The getTrackById(id) method must return the first TextTrack in the // TextTrackList object whose id IDL attribute would return a value equal // to the value of the id argument. for (unsigned i = 0; i < length(); ++i) { TextTrack* track = anonymousIndexedGetter(i); if (String(track->id()) == id) return track; } // When no tracks match the given argument, the method must return null. return 0; }
void TextTrackManager::PopulatePendingList() { uint32_t len = mTextTracks->Length(); bool dummy; for (uint32_t index = 0; index < len; ++index) { TextTrack* ttrack = mTextTracks->IndexedGetter(index, dummy); if (ttrack && ttrack->Mode() != TextTrackMode::Disabled && ttrack->ReadyState() == TextTrackReadyState::Loading) { mPendingTextTracks->AddTextTrack(ttrack, CompareTextTracks(mMediaElement)); } } }
int TextTrackList::getTrackIndex(TextTrack& textTrack) { if (is<LoadableTextTrack>(textTrack)) return downcast<LoadableTextTrack>(textTrack).trackElementIndex(); if (textTrack.trackType() == TextTrack::AddTrack) return m_elementTracks.size() + m_addTrackTracks.find(&textTrack); if (textTrack.trackType() == TextTrack::InBand) return m_elementTracks.size() + m_addTrackTracks.size() + m_inbandTracks.find(&textTrack); ASSERT_NOT_REACHED(); return -1; }
static v8::Handle<v8::Value> removeCueCallback(const v8::Arguments& args) { if (args.Length() < 1) return throwNotEnoughArgumentsError(args.GetIsolate()); TextTrack* imp = V8TextTrack::toNative(args.Holder()); ExceptionCode ec = 0; { V8TRYCATCH(TextTrackCue*, cue, V8TextTrackCue::HasInstance(MAYBE_MISSING_PARAMETER(args, 0, DefaultIsUndefined)) ? V8TextTrackCue::toNative(v8::Handle<v8::Object>::Cast(MAYBE_MISSING_PARAMETER(args, 0, DefaultIsUndefined))) : 0); imp->removeCue(cue, ec); if (UNLIKELY(ec)) goto fail; return v8Undefined(); } fail: return setDOMException(ec, args.GetIsolate()); }
static v8::Handle<v8::Value> dispatchEventCallback(const v8::Arguments& args) { if (args.Length() < 1) return throwNotEnoughArgumentsError(args.GetIsolate()); TextTrack* imp = V8TextTrack::toNative(args.Holder()); ExceptionCode ec = 0; { V8TRYCATCH(Event*, evt, V8Event::HasInstance(MAYBE_MISSING_PARAMETER(args, 0, DefaultIsUndefined)) ? V8Event::toNative(v8::Handle<v8::Object>::Cast(MAYBE_MISSING_PARAMETER(args, 0, DefaultIsUndefined))) : 0); bool result = imp->dispatchEvent(evt, ec); if (UNLIKELY(ec)) goto fail; return v8Boolean(result, args.GetIsolate()); } fail: return setDOMException(ec, args.GetIsolate()); }
void TextTrackList::invalidateTrackIndexesAfterTrack(TextTrack& track) { Vector<RefPtr<TrackBase>>* tracks = nullptr; switch (track.trackType()) { case TextTrack::TrackElement: tracks = &m_elementTracks; for (auto& addTrack : m_addTrackTracks) downcast<TextTrack>(addTrack.get())->invalidateTrackIndex(); for (auto& inbandTrack : m_inbandTracks) downcast<TextTrack>(inbandTrack.get())->invalidateTrackIndex(); break; case TextTrack::AddTrack: tracks = &m_addTrackTracks; for (auto& inbandTrack : m_inbandTracks) downcast<TextTrack>(inbandTrack.get())->invalidateTrackIndex(); break; case TextTrack::InBand: tracks = &m_inbandTracks; break; default: ASSERT_NOT_REACHED(); } size_t index = tracks->find(&track); if (index == notFound) return; for (size_t i = index; i < tracks->size(); ++i) downcast<TextTrack>(*tracks->at(index)).invalidateTrackIndex(); }
void MediaControlClosedCaptionsTrackListElement::updateDisplay() { #if ENABLE(VIDEO_TRACK) DEFINE_STATIC_LOCAL(AtomicString, selectedClassValue, ("selected", AtomicString::ConstructFromLiteral)); if (!mediaController()->hasClosedCaptions()) return; HTMLMediaElement* mediaElement = toParentMediaElement(this); if (!mediaElement) return; TextTrackList* trackList = mediaElement->textTracks(); if (!trackList || !trackList->length()) return; if (m_trackListHasChanged) rebuildTrackListMenu(); bool captionsVisible = mediaElement->closedCaptionsVisible(); for (unsigned i = 0, length = m_menuItems.size(); i < length; ++i) { RefPtr<Element> trackItem = m_menuItems[i]; int trackIndex = trackListIndexForElement(trackItem.get()); if (trackIndex != HTMLMediaElement::textTracksIndexNotFound()) { if (trackIndex == HTMLMediaElement::textTracksOffIndex()) { if (captionsVisible) trackItem->classList()->remove(selectedClassValue, ASSERT_NO_EXCEPTION); else trackItem->classList()->add(selectedClassValue, ASSERT_NO_EXCEPTION); } else { TextTrack* track = trackList->item(trackIndex); if (!track) continue; if (track->mode() == TextTrack::showingKeyword()) trackItem->classList()->add(selectedClassValue, ASSERT_NO_EXCEPTION); else trackItem->classList()->remove(selectedClassValue, ASSERT_NO_EXCEPTION); } } } #endif }
void MediaControlTextTrackListElement::refreshTextTrackListMenu() { if (!mediaElement().hasClosedCaptions() || !mediaElement().textTracksAreReady()) return; EventDispatchForbiddenScope::AllowUserAgentEvents allowEvents; removeChildren(OmitSubtreeModifiedEvent); // Construct a menu for subtitles and captions // Pass in a nullptr to createTextTrackListItem to create the "Off" track item. appendChild(createTextTrackListItem(nullptr)); TextTrackList* trackList = mediaElement().textTracks(); for (unsigned i = 0; i < trackList->length(); i++) { TextTrack* track = trackList->anonymousIndexedGetter(i); if (!track->canBeRendered()) continue; appendChild(createTextTrackListItem(track)); } }
Vector<RefPtr<TextTrack>> CaptionUserPreferences::sortedTrackListForMenu(TextTrackList* trackList) { ASSERT(trackList); Vector<RefPtr<TextTrack>> tracksForMenu; for (unsigned i = 0, length = trackList->length(); i < length; ++i) { TextTrack* track = trackList->item(i); const AtomicString& kind = track->kind(); if (kind == TextTrack::captionsKeyword() || kind == TextTrack::descriptionsKeyword() || kind == TextTrack::subtitlesKeyword()) tracksForMenu.append(track); } std::sort(tracksForMenu.begin(), tracksForMenu.end(), [](const RefPtr<TextTrack>& a, const RefPtr<TextTrack>& b) { return codePointCompare(trackDisplayName(a.get()), trackDisplayName(b.get())) < 0; }); tracksForMenu.insert(0, TextTrack::captionMenuOffItem()); tracksForMenu.insert(1, TextTrack::captionMenuAutomaticItem()); return tracksForMenu; }
void TextTrack::addCue(PassRefPtr<TextTrackCue> prpCue, ExceptionCode& ec) { if (!prpCue) return; RefPtr<TextTrackCue> cue = prpCue; // 4.7.10.12.6 Text tracks exposing in-band metadata // The UA will use DataCue to expose only text track cue objects that belong to a text track that has a text // track kind of metadata. // If a DataCue is added to a TextTrack via the addCue() method but the text track does not have its text // track kind set to metadata, throw a InvalidNodeTypeError exception and don't add the cue to the TextTrackList // of the TextTrack. if (cue->cueType() == TextTrackCue::Data && kind() != metadataKeyword()) { ec = INVALID_NODE_TYPE_ERR; return; } // TODO(93143): Add spec-compliant behavior for negative time values. if (!cue->startMediaTime().isValid() || !cue->endMediaTime().isValid() || cue->startMediaTime() < MediaTime::zeroTime() || cue->endMediaTime() < MediaTime::zeroTime()) return; // 4.8.10.12.5 Text track API // The addCue(cue) method of TextTrack objects, when invoked, must run the following steps: // 1. If the given cue is in a text track list of cues, then remove cue from that text track // list of cues. TextTrack* cueTrack = cue->track(); if (cueTrack && cueTrack != this) cueTrack->removeCue(cue.get(), ASSERT_NO_EXCEPTION); // 2. Add cue to the method's TextTrack object's text track's text track list of cues. cue->setTrack(this); ensureTextTrackCueList()->add(cue); if (m_client) m_client->textTrackAddCue(this, cue.get()); }
void AutomaticTrackSelection::perform(TextTrackList& textTracks) { TrackGroup captionAndSubtitleTracks(TrackGroup::CaptionsAndSubtitles); TrackGroup descriptionTracks(TrackGroup::Description); TrackGroup chapterTracks(TrackGroup::Chapter); TrackGroup metadataTracks(TrackGroup::Metadata); for (size_t i = 0; i < textTracks.length(); ++i) { TextTrack* textTrack = textTracks.item(i); if (!textTrack) continue; String kind = textTrack->kind(); TrackGroup* currentGroup; if (kind == TextTrack::subtitlesKeyword() || kind == TextTrack::captionsKeyword()) { currentGroup = &captionAndSubtitleTracks; } else if (kind == TextTrack::descriptionsKeyword()) { currentGroup = &descriptionTracks; } else if (kind == TextTrack::chaptersKeyword()) { currentGroup = &chapterTracks; } else { ASSERT(kind == TextTrack::metadataKeyword()); currentGroup = &metadataTracks; } if (!currentGroup->visibleTrack && textTrack->mode() == TextTrack::showingKeyword()) currentGroup->visibleTrack = textTrack; if (!currentGroup->defaultTrack && textTrack->isDefault()) currentGroup->defaultTrack = textTrack; // Do not add this track to the group if it has already been automatically configured // as we only want to perform selection once per track so that adding another track // after the initial configuration doesn't reconfigure every track - only those that // should be changed by the new addition. For example all metadata tracks are // disabled by default, and we don't want a track that has been enabled by script // to be disabled automatically when a new metadata track is added later. if (textTrack->hasBeenConfigured()) continue; if (textTrack->language().length()) currentGroup->hasSrcLang = true; currentGroup->tracks.append(textTrack); } if (captionAndSubtitleTracks.tracks.size()) performAutomaticTextTrackSelection(captionAndSubtitleTracks); if (descriptionTracks.tracks.size()) performAutomaticTextTrackSelection(descriptionTracks); if (chapterTracks.tracks.size()) performAutomaticTextTrackSelection(chapterTracks); if (metadataTracks.tracks.size()) enableDefaultMetadataTextTracks(metadataTracks); }
void MediaSource::removeSourceBuffer(SourceBuffer& buffer, ExceptionCode& ec) { LOG(MediaSource, "MediaSource::removeSourceBuffer() %p", this); Ref<SourceBuffer> protect(buffer); // 2. If sourceBuffer specifies an object that is not in sourceBuffers then // throw a NOT_FOUND_ERR exception and abort these steps. if (!m_sourceBuffers->length() || !m_sourceBuffers->contains(buffer)) { ec = NOT_FOUND_ERR; return; } // 3. If the sourceBuffer.updating attribute equals true, then run the following steps: ... buffer.abortIfUpdating(); // 4. Let SourceBuffer audioTracks list equal the AudioTrackList object returned by sourceBuffer.audioTracks. RefPtr<AudioTrackList> audioTracks = buffer.audioTracks(); // 5. If the SourceBuffer audioTracks list is not empty, then run the following steps: if (audioTracks->length()) { // 5.1 Let HTMLMediaElement audioTracks list equal the AudioTrackList object returned by the audioTracks // attribute on the HTMLMediaElement. // 5.2 Let the removed enabled audio track flag equal false. bool removedEnabledAudioTrack = false; // 5.3 For each AudioTrack object in the SourceBuffer audioTracks list, run the following steps: while (audioTracks->length()) { AudioTrack* track = audioTracks->lastItem(); // 5.3.1 Set the sourceBuffer attribute on the AudioTrack object to null. track->setSourceBuffer(nullptr); // 5.3.2 If the enabled attribute on the AudioTrack object is true, then set the removed enabled // audio track flag to true. if (track->enabled()) removedEnabledAudioTrack = true; // 5.3.3 Remove the AudioTrack object from the HTMLMediaElement audioTracks list. // 5.3.4 Queue a task to fire a trusted event named removetrack, that does not bubble and is not // cancelable, and that uses the TrackEvent interface, at the HTMLMediaElement audioTracks list. if (mediaElement()) mediaElement()->removeAudioTrack(track); // 5.3.5 Remove the AudioTrack object from the SourceBuffer audioTracks list. // 5.3.6 Queue a task to fire a trusted event named removetrack, that does not bubble and is not // cancelable, and that uses the TrackEvent interface, at the SourceBuffer audioTracks list. audioTracks->remove(track); } // 5.4 If the removed enabled audio track flag equals true, then queue a task to fire a simple event // named change at the HTMLMediaElement audioTracks list. if (removedEnabledAudioTrack) mediaElement()->audioTracks()->scheduleChangeEvent(); } // 6. Let SourceBuffer videoTracks list equal the VideoTrackList object returned by sourceBuffer.videoTracks. RefPtr<VideoTrackList> videoTracks = buffer.videoTracks(); // 7. If the SourceBuffer videoTracks list is not empty, then run the following steps: if (videoTracks->length()) { // 7.1 Let HTMLMediaElement videoTracks list equal the VideoTrackList object returned by the videoTracks // attribute on the HTMLMediaElement. // 7.2 Let the removed selected video track flag equal false. bool removedSelectedVideoTrack = false; // 7.3 For each VideoTrack object in the SourceBuffer videoTracks list, run the following steps: while (videoTracks->length()) { VideoTrack* track = videoTracks->lastItem(); // 7.3.1 Set the sourceBuffer attribute on the VideoTrack object to null. track->setSourceBuffer(nullptr); // 7.3.2 If the selected attribute on the VideoTrack object is true, then set the removed selected // video track flag to true. if (track->selected()) removedSelectedVideoTrack = true; // 7.3.3 Remove the VideoTrack object from the HTMLMediaElement videoTracks list. // 7.3.4 Queue a task to fire a trusted event named removetrack, that does not bubble and is not // cancelable, and that uses the TrackEvent interface, at the HTMLMediaElement videoTracks list. if (mediaElement()) mediaElement()->removeVideoTrack(track); // 7.3.5 Remove the VideoTrack object from the SourceBuffer videoTracks list. // 7.3.6 Queue a task to fire a trusted event named removetrack, that does not bubble and is not // cancelable, and that uses the TrackEvent interface, at the SourceBuffer videoTracks list. videoTracks->remove(track); } // 7.4 If the removed selected video track flag equals true, then queue a task to fire a simple event // named change at the HTMLMediaElement videoTracks list. if (removedSelectedVideoTrack) mediaElement()->videoTracks()->scheduleChangeEvent(); } // 8. Let SourceBuffer textTracks list equal the TextTrackList object returned by sourceBuffer.textTracks. RefPtr<TextTrackList> textTracks = buffer.textTracks(); // 9. If the SourceBuffer textTracks list is not empty, then run the following steps: if (textTracks->length()) { // 9.1 Let HTMLMediaElement textTracks list equal the TextTrackList object returned by the textTracks // attribute on the HTMLMediaElement. // 9.2 Let the removed enabled text track flag equal false. bool removedEnabledTextTrack = false; // 9.3 For each TextTrack object in the SourceBuffer textTracks list, run the following steps: while (textTracks->length()) { TextTrack* track = textTracks->lastItem(); // 9.3.1 Set the sourceBuffer attribute on the TextTrack object to null. track->setSourceBuffer(nullptr); // 9.3.2 If the mode attribute on the TextTrack object is set to "showing" or "hidden", then // set the removed enabled text track flag to true. if (track->mode() == TextTrack::showingKeyword() || track->mode() == TextTrack::hiddenKeyword()) removedEnabledTextTrack = true; // 9.3.3 Remove the TextTrack object from the HTMLMediaElement textTracks list. // 9.3.4 Queue a task to fire a trusted event named removetrack, that does not bubble and is not // cancelable, and that uses the TrackEvent interface, at the HTMLMediaElement textTracks list. if (mediaElement()) mediaElement()->removeTextTrack(track); // 9.3.5 Remove the TextTrack object from the SourceBuffer textTracks list. // 9.3.6 Queue a task to fire a trusted event named removetrack, that does not bubble and is not // cancelable, and that uses the TrackEvent interface, at the SourceBuffer textTracks list. textTracks->remove(track); } // 9.4 If the removed enabled text track flag equals true, then queue a task to fire a simple event // named change at the HTMLMediaElement textTracks list. if (removedEnabledTextTrack) mediaElement()->textTracks()->scheduleChangeEvent(); } // 10. If sourceBuffer is in activeSourceBuffers, then remove sourceBuffer from activeSourceBuffers ... m_activeSourceBuffers->remove(buffer); // 11. Remove sourceBuffer from sourceBuffers and fire a removesourcebuffer event // on that object. m_sourceBuffers->remove(buffer); // 12. Destroy all resources for sourceBuffer. buffer.removedFromMediaSource(); }
static v8::Handle<v8::Value> oncuechangeAttrGetter(v8::Local<v8::String> name, const v8::AccessorInfo& info) { TextTrack* imp = V8TextTrack::toNative(info.Holder()); return imp->oncuechange() ? v8::Handle<v8::Value>(static_cast<V8AbstractEventListener*>(imp->oncuechange())->getListenerObject(imp->scriptExecutionContext())) : v8::Handle<v8::Value>(v8Null(info.GetIsolate())); }
void AutomaticTrackSelection::performAutomaticTextTrackSelection(const TrackGroup& group) { ASSERT(group.tracks.size()); // First, find the track in the group that should be enabled (if any). HeapVector<Member<TextTrack>> currentlyEnabledTracks; TextTrack* trackToEnable = nullptr; TextTrack* defaultTrack = nullptr; TextTrack* preferredTrack = nullptr; TextTrack* fallbackTrack = nullptr; int highestTrackScore = 0; for (size_t i = 0; i < group.tracks.size(); ++i) { TextTrack* textTrack = group.tracks[i]; if (m_configuration.disableCurrentlyEnabledTracks && textTrack->mode() == TextTrack::showingKeyword()) currentlyEnabledTracks.append(textTrack); int trackScore = textTrackSelectionScore(*textTrack); if (textTrack->kind() == preferredTrackKind()) trackScore += 1; if (trackScore) { // * If the text track kind is subtitles or captions and the user has indicated an interest in having a // track with this text track kind, text track language, and text track label enabled, and there is no // other text track in the media element's list of text tracks with a text track kind of either subtitles // or captions whose text track mode is showing // Let the text track mode be showing. if (trackScore > highestTrackScore) { preferredTrack = textTrack; highestTrackScore = trackScore; } if (!defaultTrack && textTrack->isDefault()) defaultTrack = textTrack; if (!fallbackTrack) fallbackTrack = textTrack; } else if (!group.visibleTrack && !defaultTrack && textTrack->isDefault()) { // * If the track element has a default attribute specified, and there is no other text track in the media // element's list of text tracks whose text track mode is showing or showing by default // Let the text track mode be showing by default. defaultTrack = textTrack; } } if (m_configuration.textTrackKindUserPreference != TextTrackKindUserPreference::Default) trackToEnable = preferredTrack; if (!trackToEnable && defaultTrack) trackToEnable = defaultTrack; if (!trackToEnable && m_configuration.forceEnableSubtitleOrCaptionTrack && group.kind == TrackGroup::CaptionsAndSubtitles) { if (fallbackTrack) trackToEnable = fallbackTrack; else trackToEnable = group.tracks[0]; } if (currentlyEnabledTracks.size()) { for (size_t i = 0; i < currentlyEnabledTracks.size(); ++i) { TextTrack* textTrack = currentlyEnabledTracks[i]; if (textTrack != trackToEnable) textTrack->setMode(TextTrack::disabledKeyword()); } } if (trackToEnable) trackToEnable->setMode(TextTrack::showingKeyword()); }
static v8::Handle<v8::Value> modeAttrGetter(v8::Local<v8::String> name, const v8::AccessorInfo& info) { TextTrack* imp = V8TextTrack::toNative(info.Holder()); return v8String(imp->mode(), info.GetIsolate()); }
// 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(); }