@@ -823,6 +823,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
823
823
/** @private {!Array<shaka.extern.Stream>} */
824
824
this . externalSrcEqualsThumbnailsStreams_ = [ ] ;
825
825
826
+ /** @private {!Array<shaka.extern.Stream>} */
827
+ this . externalChaptersStreams_ = [ ] ;
828
+
826
829
/** @private {number} */
827
830
this . completionPercent_ = - 1 ;
828
831
@@ -1591,6 +1594,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1591
1594
this . rebufferingCount_ = - 1 ;
1592
1595
1593
1596
this . externalSrcEqualsThumbnailsStreams_ = [ ] ;
1597
+ this . externalChaptersStreams_ = [ ] ;
1594
1598
1595
1599
this . completionPercent_ = - 1 ;
1596
1600
@@ -6213,13 +6217,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
6213
6217
* @export
6214
6218
*/
6215
6219
getChaptersTracks ( ) {
6216
- if ( this . video_ && this . video_ . currentSrc && this . video_ . textTracks ) {
6217
- const textTracks = this . getChaptersTracks_ ( ) ;
6218
- const StreamUtils = shaka . util . StreamUtils ;
6219
- return textTracks . map ( ( text ) => StreamUtils . html5TextTrackToTrack ( text ) ) ;
6220
- } else {
6221
- return [ ] ;
6222
- }
6220
+ return this . externalChaptersStreams_ . map (
6221
+ ( text ) => shaka . util . StreamUtils . textStreamToTrack ( text ) ) ;
6223
6222
}
6224
6223
6225
6224
/**
@@ -6230,38 +6229,35 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
6230
6229
* @export
6231
6230
*/
6232
6231
getChapters ( language ) {
6233
- if ( ! this . video_ || ! this . video_ . currentSrc || ! this . video_ . textTracks ) {
6232
+ if ( ! this . externalChaptersStreams_ . length ) {
6234
6233
return [ ] ;
6235
6234
}
6236
6235
const LanguageUtils = shaka . util . LanguageUtils ;
6237
6236
const inputLanguage = LanguageUtils . normalize ( language ) ;
6238
- const chaptersTracks = this . getChaptersTracks_ ( ) ;
6239
- const chaptersTracksWithLanguage = chaptersTracks
6240
- . filter ( ( t ) => LanguageUtils . normalize ( t . language ) == inputLanguage ) ;
6241
- if ( ! chaptersTracksWithLanguage || ! chaptersTracksWithLanguage . length ) {
6237
+ const chapterStreams = this . externalChaptersStreams_
6238
+ . filter ( ( c ) => LanguageUtils . normalize ( c . language ) == inputLanguage ) ;
6239
+ if ( ! chapterStreams . length ) {
6242
6240
return [ ] ;
6243
6241
}
6244
6242
const chapters = [ ] ;
6245
6243
const uniqueChapters = new Set ( ) ;
6246
- for ( const chaptersTrack of chaptersTracksWithLanguage ) {
6247
- if ( chaptersTrack && chaptersTrack . cues ) {
6248
- for ( const cue of chaptersTrack . cues ) {
6249
- let id = cue . id ;
6250
- if ( ! id || id == '' ) {
6251
- id = cue . startTime + '-' + cue . endTime + '-' + cue . text ;
6252
- }
6244
+ for ( const chapterStream of chapterStreams ) {
6245
+ if ( chapterStream . segmentIndex ) {
6246
+ chapterStream . segmentIndex . forEachTopLevelReference ( ( ref ) => {
6247
+ const title = ref . getUris ( ) [ 0 ] ;
6248
+ const id = ref . startTime + '-' + ref . endTime + '-' + title ;
6253
6249
/** @type {shaka.extern.Chapter } */
6254
6250
const chapter = {
6255
- id : id ,
6256
- title : cue . text ,
6257
- startTime : cue . startTime ,
6258
- endTime : cue . endTime ,
6251
+ id,
6252
+ title,
6253
+ startTime : ref . startTime ,
6254
+ endTime : ref . endTime ,
6259
6255
} ;
6260
6256
if ( ! uniqueChapters . has ( id ) ) {
6261
6257
chapters . push ( chapter ) ;
6262
6258
uniqueChapters . add ( id ) ;
6263
6259
}
6264
- }
6260
+ } ) ;
6265
6261
}
6266
6262
}
6267
6263
return chapters ;
@@ -6308,19 +6304,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
6308
6304
. filter ( ( t ) => t . kind == 'metadata' ) ;
6309
6305
}
6310
6306
6311
- /**
6312
- * Get the TextTracks with the 'chapters' kind.
6313
- *
6314
- * @return {!Array<TextTrack> }
6315
- * @private
6316
- */
6317
- getChaptersTracks_ ( ) {
6318
- goog . asserts . assert ( this . video_ . textTracks ,
6319
- 'TextTracks should be valid.' ) ;
6320
- return Array . from ( this . video_ . textTracks )
6321
- . filter ( ( t ) => t . kind == 'chapters' ) ;
6322
- }
6323
-
6324
6307
/**
6325
6308
* Enable or disable the text displayer. If the player is in an unloaded
6326
6309
* state, the request will be applied next time content is loaded.
@@ -6985,47 +6968,100 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
6985
6968
mimeType = await this . getTextMimetype_ ( uri ) ;
6986
6969
}
6987
6970
6988
- let adCuePoints = [ ] ;
6989
- if ( this . adManager_ ) {
6990
- adCuePoints = this . adManager_ . getCuePoints ( ) ;
6971
+ const ContentType = shaka . util . ManifestParserUtils . ContentType ;
6972
+ const seekRange = this . seekRange ( ) ;
6973
+ let duration = seekRange . end - seekRange . start ;
6974
+ if ( this . manifest_ ) {
6975
+ duration = this . manifest_ . presentationTimeline . getDuration ( ) ;
6976
+ }
6977
+ if ( duration == Infinity ) {
6978
+ throw new shaka . util . Error (
6979
+ shaka . util . Error . Severity . RECOVERABLE ,
6980
+ shaka . util . Error . Category . MANIFEST ,
6981
+ shaka . util . Error . Code . CANNOT_ADD_EXTERNAL_CHAPTERS_TO_LIVE_STREAM ) ;
6991
6982
}
6992
6983
6993
- /** @type {!HTMLTrackElement } */
6994
- const trackElement = await this . addSrcTrackElement_ (
6995
- uri , language , /* kind= */ 'chapters' , mimeType , /* label= */ '' ,
6996
- adCuePoints ) ;
6984
+ goog . asserts . assert (
6985
+ this . networkingEngine_ , 'Need networking engine.' ) ;
6986
+ const buffer = await this . getTextData_ ( uri ,
6987
+ this . networkingEngine_ ,
6988
+ this . config_ . streaming . retryParameters ) ;
6997
6989
6998
- const chaptersTracks = this . getChaptersTracks ( ) ;
6999
- const chaptersTrack = chaptersTracks . find ( ( t ) => {
7000
- return t . language == language ;
7001
- } ) ;
6990
+ const factory = shaka . text . TextEngine . findParser ( mimeType ) ;
6991
+ if ( ! factory ) {
6992
+ throw new shaka . util . Error (
6993
+ shaka . util . Error . Severity . CRITICAL ,
6994
+ shaka . util . Error . Category . TEXT ,
6995
+ shaka . util . Error . Code . MISSING_TEXT_PLUGIN ,
6996
+ mimeType ) ;
6997
+ }
6998
+ const textParser = factory ( ) ;
6999
+ const time = {
7000
+ periodStart : 0 ,
7001
+ segmentStart : 0 ,
7002
+ segmentEnd : duration ,
7003
+ vttOffset : 0 ,
7004
+ } ;
7005
+ const data = shaka . util . BufferUtils . toUint8 ( buffer ) ;
7006
+ const cues = textParser . parseMedia ( data , time , uri , /* images= */ [ ] ) ;
7002
7007
7003
- if ( chaptersTrack ) {
7004
- await new Promise ( ( resolve , reject ) => {
7005
- // The chapter data isn't available until the 'load' event fires, and
7006
- // that won't happen until the chapters track is activated by the
7007
- // activateChaptersTrack_ method.
7008
- this . loadEventManager_ . listenOnce ( trackElement , 'load' , resolve ) ;
7009
- this . loadEventManager_ . listenOnce ( trackElement , 'error' , ( event ) => {
7010
- reject ( new shaka . util . Error (
7011
- shaka . util . Error . Severity . RECOVERABLE ,
7012
- shaka . util . Error . Category . TEXT ,
7013
- shaka . util . Error . Code . CHAPTERS_TRACK_FAILED ) ) ;
7014
- } ) ;
7015
- } ) ;
7008
+ const references = [ ] ;
7009
+ for ( const cue of cues ) {
7010
+ const reference = new shaka . media . SegmentReference (
7011
+ cue . startTime ,
7012
+ cue . endTime ,
7013
+ ( ) => [ cue . payload ] ,
7014
+ /* startByte= */ 0 ,
7015
+ /* endByte= */ null ,
7016
+ /* initSegmentReference= */ null ,
7017
+ /* timestampOffset= */ 0 ,
7018
+ /* appendWindowStart= */ 0 ,
7019
+ /* appendWindowEnd= */ Infinity ,
7020
+ ) ;
7021
+ references . push ( reference ) ;
7022
+ }
7016
7023
7017
- this . onTracksChanged_ ( ) ;
7024
+ const chaptersMimeType = 'text/plain' ;
7018
7025
7019
- return chaptersTrack ;
7020
- }
7026
+ /** @type {shaka.extern.Stream } */
7027
+ const stream = {
7028
+ id : this . nextExternalStreamId_ ++ ,
7029
+ originalId : null ,
7030
+ groupId : null ,
7031
+ createSegmentIndex : ( ) => Promise . resolve ( ) ,
7032
+ segmentIndex : new shaka . media . SegmentIndex ( references ) ,
7033
+ mimeType : chaptersMimeType ,
7034
+ codecs : '' ,
7035
+ kind : '' ,
7036
+ encrypted : false ,
7037
+ drmInfos : [ ] ,
7038
+ keyIds : new Set ( ) ,
7039
+ language : language ,
7040
+ originalLanguage : language ,
7041
+ label : null ,
7042
+ type : ContentType . TEXT ,
7043
+ primary : false ,
7044
+ trickModeVideo : null ,
7045
+ dependencyStream : null ,
7046
+ emsgSchemeIdUris : null ,
7047
+ roles : [ ] ,
7048
+ forced : false ,
7049
+ channelsCount : null ,
7050
+ audioSamplingRate : null ,
7051
+ spatialAudio : false ,
7052
+ closedCaptions : null ,
7053
+ accessibilityPurpose : null ,
7054
+ external : true ,
7055
+ fastSwitching : false ,
7056
+ fullMimeTypes : new Set ( [ shaka . util . MimeUtils . getFullType (
7057
+ chaptersMimeType , '' ) ] ) ,
7058
+ isAudioMuxedInVideo : false ,
7059
+ baseOriginalId : null ,
7060
+ } ;
7021
7061
7022
- // This should not happen, but there are browser implementations that may
7023
- // not support the Track element.
7024
- shaka . log . error ( 'Cannot add this text when loaded with src=' ) ;
7025
- throw new shaka . util . Error (
7026
- shaka . util . Error . Severity . RECOVERABLE ,
7027
- shaka . util . Error . Category . TEXT ,
7028
- shaka . util . Error . Code . CANNOT_ADD_EXTERNAL_TEXT_TO_SRC_EQUALS ) ;
7062
+ this . externalChaptersStreams_ . push ( stream ) ;
7063
+ this . onTracksChanged_ ( ) ;
7064
+ return shaka . util . StreamUtils . textStreamToTrack ( stream ) ;
7029
7065
}
7030
7066
7031
7067
/**
0 commit comments