Skip to content
Toggle navigation
Toggle navigation
This project
Loading...
Sign in
brainfood
/
videojs-contrib-hls
Go to a project
Toggle navigation
Toggle navigation pinning
Projects
Groups
Snippets
Help
Project
Activity
Repository
Graphs
Network
Create a new issue
Commits
Issue Boards
Files
Commits
Network
Compare
Branches
Tags
b5e60aba
authored
2016-02-26 14:44:53 -0500
by
jrivera
Browse Files
Options
Browse Files
Tag
Download
Email Patches
Plain Diff
Fix whitespace - tabs to spaces!
1 parent
2db4b64c
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
466 additions
and
466 deletions
src/playlist-loader.js
src/playlist-loader.js
View file @
b5e60ab
...
...
@@ -87,378 +87,378 @@ const updateSegments = function(original, update, offset) {
export
default
class
PlaylistLoader
extends
Stream
{
constructor
(
srcUrl
,
withCredentials
)
{
super
();
let
loader
=
this
;
let
dispose
;
let
mediaUpdateTimeout
;
let
request
;
let
playlistRequestError
;
let
haveMetadata
;
// a flag that disables "expired time"-tracking this setting has
// no effect when not playing a live stream
this
.
trackExpiredTime_
=
false
;
if
(
!
srcUrl
)
{
throw
new
Error
(
'A non-empty playlist URL is required'
);
}
playlistRequestError
=
function
(
xhr
,
url
,
startingState
)
{
loader
.
setBandwidth
(
request
||
xhr
);
// any in-flight request is now finished
request
=
null
;
if
(
startingState
)
{
loader
.
state
=
startingState
;
}
loader
.
error
=
{
playlist
:
loader
.
master
.
playlists
[
url
],
status
:
xhr
.
status
,
message
:
'HLS playlist request error at URL: '
+
url
,
responseText
:
xhr
.
responseText
,
code
:
(
xhr
.
status
>=
500
)
?
4
:
2
};
loader
.
trigger
(
'error'
);
};
// update the playlist loader's state in response to a new or
// updated playlist.
haveMetadata
=
function
(
xhr
,
url
)
{
let
parser
;
let
refreshDelay
;
let
update
;
loader
.
setBandwidth
(
request
||
xhr
);
// any in-flight request is now finished
request
=
null
;
loader
.
state
=
'HAVE_METADATA'
;
parser
=
new
m3u8
.
Parser
();
parser
.
push
(
xhr
.
responseText
);
parser
.
end
();
parser
.
manifest
.
uri
=
url
;
// merge this playlist into the master
update
=
updateMaster
(
loader
.
master
,
parser
.
manifest
);
refreshDelay
=
(
parser
.
manifest
.
targetDuration
||
10
)
*
1000
;
if
(
update
)
{
loader
.
master
=
update
;
loader
.
updateMediaPlaylist_
(
parser
.
manifest
);
}
else
{
// if the playlist is unchanged since the last reload,
// try again after half the target duration
refreshDelay
/=
2
;
}
// refresh live playlists after a target duration passes
if
(
!
loader
.
media
().
endList
)
{
window
.
clearTimeout
(
mediaUpdateTimeout
);
mediaUpdateTimeout
=
window
.
setTimeout
(
function
()
{
loader
.
trigger
(
'mediaupdatetimeout'
);
},
refreshDelay
);
}
loader
.
trigger
(
'loadedplaylist'
);
};
// initialize the loader state
loader
.
state
=
'HAVE_NOTHING'
;
// track the time that has expired from the live window
// this allows the seekable start range to be calculated even if
// all segments with timing information have expired
this
.
expired_
=
0
;
// capture the prototype dispose function
dispose
=
this
.
dispose
;
/**
* Abort any outstanding work and clean up.
*/
loader
.
dispose
=
function
()
{
if
(
request
)
{
request
.
onreadystatechange
=
null
;
request
.
abort
();
request
=
null
;
}
window
.
clearTimeout
(
mediaUpdateTimeout
);
dispose
.
call
(
this
);
};
/**
* When called without any arguments, returns the currently
* active media playlist. When called with a single argument,
* triggers the playlist loader to asynchronously switch to the
* specified media playlist. Calling this method while the
* loader is in the HAVE_NOTHING causes an error to be emitted
* but otherwise has no effect.
* @param playlist (optional) {object} the parsed media playlist
* object to switch to
*/
loader
.
media
=
function
(
playlist
)
{
let
startingState
=
loader
.
state
;
let
mediaChange
;
// getter
if
(
!
playlist
)
{
return
loader
.
media_
;
}
// setter
if
(
loader
.
state
===
'HAVE_NOTHING'
)
{
throw
new
Error
(
'Cannot switch media playlist from '
+
loader
.
state
);
}
// find the playlist object if the target playlist has been
// specified by URI
if
(
typeof
playlist
===
'string'
)
{
if
(
!
loader
.
master
.
playlists
[
playlist
])
{
throw
new
Error
(
'Unknown playlist URI: '
+
playlist
);
}
playlist
=
loader
.
master
.
playlists
[
playlist
];
}
mediaChange
=
!
loader
.
media_
||
playlist
.
uri
!==
loader
.
media_
.
uri
;
// switch to fully loaded playlists immediately
if
(
loader
.
master
.
playlists
[
playlist
.
uri
].
endList
)
{
// abort outstanding playlist requests
if
(
request
)
{
request
.
onreadystatechange
=
null
;
request
.
abort
();
request
=
null
;
}
loader
.
state
=
'HAVE_METADATA'
;
loader
.
media_
=
playlist
;
// trigger media change if the active media has been updated
if
(
mediaChange
)
{
loader
.
trigger
(
'mediachange'
);
}
return
;
}
// switching to the active playlist is a no-op
if
(
!
mediaChange
)
{
return
;
}
loader
.
state
=
'SWITCHING_MEDIA'
;
// there is already an outstanding playlist request
if
(
request
)
{
if
(
resolveUrl
(
loader
.
master
.
uri
,
playlist
.
uri
)
===
request
.
url
)
{
// requesting to switch to the same playlist multiple times
// has no effect after the first
return
;
}
request
.
onreadystatechange
=
null
;
request
.
abort
();
request
=
null
;
}
// request the new playlist
request
=
XhrModule
({
uri
:
resolveUrl
(
loader
.
master
.
uri
,
playlist
.
uri
),
withCredentials
},
function
(
error
,
request
)
{
if
(
error
)
{
return
playlistRequestError
(
request
,
playlist
.
uri
,
startingState
);
}
haveMetadata
(
request
,
playlist
.
uri
);
// fire loadedmetadata the first time a media playlist is loaded
if
(
startingState
===
'HAVE_MASTER'
)
{
loader
.
trigger
(
'loadedmetadata'
);
}
else
{
loader
.
trigger
(
'mediachange'
);
}
});
};
loader
.
setBandwidth
=
function
(
xhr
)
{
loader
.
bandwidth
=
xhr
.
bandwidth
;
};
// In a live list, don't keep track of the expired time until
// HLS tells us that "first play" has commenced
loader
.
on
(
'firstplay'
,
function
()
{
this
.
trackExpiredTime_
=
true
;
});
// live playlist staleness timeout
loader
.
on
(
'mediaupdatetimeout'
,
function
()
{
if
(
loader
.
state
!==
'HAVE_METADATA'
)
{
// only refresh the media playlist if no other activity is going on
return
;
}
loader
.
state
=
'HAVE_CURRENT_METADATA'
;
request
=
XhrModule
({
uri
:
resolveUrl
(
loader
.
master
.
uri
,
loader
.
media
().
uri
),
withCredentials
},
function
(
error
,
request
)
{
if
(
error
)
{
return
playlistRequestError
(
request
,
loader
.
media
().
uri
);
}
haveMetadata
(
request
,
loader
.
media
().
uri
);
});
});
// request the specified URL
request
=
XhrModule
({
uri
:
srcUrl
,
withCredentials
},
function
(
error
,
req
)
{
let
parser
;
let
i
;
// clear the loader's request reference
request
=
null
;
if
(
error
)
{
loader
.
error
=
{
status
:
req
.
status
,
message
:
'HLS playlist request error at URL: '
+
srcUrl
,
responseText
:
req
.
responseText
,
// MEDIA_ERR_NETWORK
code
:
2
};
return
loader
.
trigger
(
'error'
);
}
parser
=
new
m3u8
.
Parser
();
parser
.
push
(
req
.
responseText
);
parser
.
end
();
loader
.
state
=
'HAVE_MASTER'
;
parser
.
manifest
.
uri
=
srcUrl
;
// loaded a master playlist
if
(
parser
.
manifest
.
playlists
)
{
loader
.
master
=
parser
.
manifest
;
// setup by-URI lookups
i
=
loader
.
master
.
playlists
.
length
;
while
(
i
--
)
{
loader
.
master
.
playlists
[
loader
.
master
.
playlists
[
i
].
uri
]
=
loader
.
master
.
playlists
[
i
];
}
loader
.
trigger
(
'loadedplaylist'
);
if
(
!
request
)
{
// no media playlist was specifically selected so start
// from the first listed one
loader
.
media
(
parser
.
manifest
.
playlists
[
0
]);
}
return
;
}
// loaded a media playlist
// infer a master playlist if none was previously requested
loader
.
master
=
{
uri
:
window
.
location
.
href
,
playlists
:
[{
uri
:
srcUrl
}]
};
loader
.
master
.
playlists
[
srcUrl
]
=
loader
.
master
.
playlists
[
0
];
haveMetadata
(
req
,
srcUrl
);
return
loader
.
trigger
(
'loadedmetadata'
);
});
}
super
();
let
loader
=
this
;
let
dispose
;
let
mediaUpdateTimeout
;
let
request
;
let
playlistRequestError
;
let
haveMetadata
;
// a flag that disables "expired time"-tracking this setting has
// no effect when not playing a live stream
this
.
trackExpiredTime_
=
false
;
if
(
!
srcUrl
)
{
throw
new
Error
(
'A non-empty playlist URL is required'
);
}
playlistRequestError
=
function
(
xhr
,
url
,
startingState
)
{
loader
.
setBandwidth
(
request
||
xhr
);
// any in-flight request is now finished
request
=
null
;
if
(
startingState
)
{
loader
.
state
=
startingState
;
}
loader
.
error
=
{
playlist
:
loader
.
master
.
playlists
[
url
],
status
:
xhr
.
status
,
message
:
'HLS playlist request error at URL: '
+
url
,
responseText
:
xhr
.
responseText
,
code
:
(
xhr
.
status
>=
500
)
?
4
:
2
};
loader
.
trigger
(
'error'
);
};
// update the playlist loader's state in response to a new or
// updated playlist.
haveMetadata
=
function
(
xhr
,
url
)
{
let
parser
;
let
refreshDelay
;
let
update
;
loader
.
setBandwidth
(
request
||
xhr
);
// any in-flight request is now finished
request
=
null
;
loader
.
state
=
'HAVE_METADATA'
;
parser
=
new
m3u8
.
Parser
();
parser
.
push
(
xhr
.
responseText
);
parser
.
end
();
parser
.
manifest
.
uri
=
url
;
// merge this playlist into the master
update
=
updateMaster
(
loader
.
master
,
parser
.
manifest
);
refreshDelay
=
(
parser
.
manifest
.
targetDuration
||
10
)
*
1000
;
if
(
update
)
{
loader
.
master
=
update
;
loader
.
updateMediaPlaylist_
(
parser
.
manifest
);
}
else
{
// if the playlist is unchanged since the last reload,
// try again after half the target duration
refreshDelay
/=
2
;
}
// refresh live playlists after a target duration passes
if
(
!
loader
.
media
().
endList
)
{
window
.
clearTimeout
(
mediaUpdateTimeout
);
mediaUpdateTimeout
=
window
.
setTimeout
(
function
()
{
loader
.
trigger
(
'mediaupdatetimeout'
);
},
refreshDelay
);
}
loader
.
trigger
(
'loadedplaylist'
);
};
// initialize the loader state
loader
.
state
=
'HAVE_NOTHING'
;
// track the time that has expired from the live window
// this allows the seekable start range to be calculated even if
// all segments with timing information have expired
this
.
expired_
=
0
;
// capture the prototype dispose function
dispose
=
this
.
dispose
;
/**
* Abort any outstanding work and clean up.
*/
loader
.
dispose
=
function
()
{
if
(
request
)
{
request
.
onreadystatechange
=
null
;
request
.
abort
();
request
=
null
;
}
window
.
clearTimeout
(
mediaUpdateTimeout
);
dispose
.
call
(
this
);
};
/**
* When called without any arguments, returns the currently
* active media playlist. When called with a single argument,
* triggers the playlist loader to asynchronously switch to the
* specified media playlist. Calling this method while the
* loader is in the HAVE_NOTHING causes an error to be emitted
* but otherwise has no effect.
* @param playlist (optional) {object} the parsed media playlist
* object to switch to
*/
loader
.
media
=
function
(
playlist
)
{
let
startingState
=
loader
.
state
;
let
mediaChange
;
// getter
if
(
!
playlist
)
{
return
loader
.
media_
;
}
// setter
if
(
loader
.
state
===
'HAVE_NOTHING'
)
{
throw
new
Error
(
'Cannot switch media playlist from '
+
loader
.
state
);
}
// find the playlist object if the target playlist has been
// specified by URI
if
(
typeof
playlist
===
'string'
)
{
if
(
!
loader
.
master
.
playlists
[
playlist
])
{
throw
new
Error
(
'Unknown playlist URI: '
+
playlist
);
}
playlist
=
loader
.
master
.
playlists
[
playlist
];
}
mediaChange
=
!
loader
.
media_
||
playlist
.
uri
!==
loader
.
media_
.
uri
;
// switch to fully loaded playlists immediately
if
(
loader
.
master
.
playlists
[
playlist
.
uri
].
endList
)
{
// abort outstanding playlist requests
if
(
request
)
{
request
.
onreadystatechange
=
null
;
request
.
abort
();
request
=
null
;
}
loader
.
state
=
'HAVE_METADATA'
;
loader
.
media_
=
playlist
;
// trigger media change if the active media has been updated
if
(
mediaChange
)
{
loader
.
trigger
(
'mediachange'
);
}
return
;
}
// switching to the active playlist is a no-op
if
(
!
mediaChange
)
{
return
;
}
loader
.
state
=
'SWITCHING_MEDIA'
;
// there is already an outstanding playlist request
if
(
request
)
{
if
(
resolveUrl
(
loader
.
master
.
uri
,
playlist
.
uri
)
===
request
.
url
)
{
// requesting to switch to the same playlist multiple times
// has no effect after the first
return
;
}
request
.
onreadystatechange
=
null
;
request
.
abort
();
request
=
null
;
}
// request the new playlist
request
=
XhrModule
({
uri
:
resolveUrl
(
loader
.
master
.
uri
,
playlist
.
uri
),
withCredentials
},
function
(
error
,
request
)
{
if
(
error
)
{
return
playlistRequestError
(
request
,
playlist
.
uri
,
startingState
);
}
haveMetadata
(
request
,
playlist
.
uri
);
// fire loadedmetadata the first time a media playlist is loaded
if
(
startingState
===
'HAVE_MASTER'
)
{
loader
.
trigger
(
'loadedmetadata'
);
}
else
{
loader
.
trigger
(
'mediachange'
);
}
});
};
loader
.
setBandwidth
=
function
(
xhr
)
{
loader
.
bandwidth
=
xhr
.
bandwidth
;
};
// In a live list, don't keep track of the expired time until
// HLS tells us that "first play" has commenced
loader
.
on
(
'firstplay'
,
function
()
{
this
.
trackExpiredTime_
=
true
;
});
// live playlist staleness timeout
loader
.
on
(
'mediaupdatetimeout'
,
function
()
{
if
(
loader
.
state
!==
'HAVE_METADATA'
)
{
// only refresh the media playlist if no other activity is going on
return
;
}
loader
.
state
=
'HAVE_CURRENT_METADATA'
;
request
=
XhrModule
({
uri
:
resolveUrl
(
loader
.
master
.
uri
,
loader
.
media
().
uri
),
withCredentials
},
function
(
error
,
request
)
{
if
(
error
)
{
return
playlistRequestError
(
request
,
loader
.
media
().
uri
);
}
haveMetadata
(
request
,
loader
.
media
().
uri
);
});
});
// request the specified URL
request
=
XhrModule
({
uri
:
srcUrl
,
withCredentials
},
function
(
error
,
req
)
{
let
parser
;
let
i
;
// clear the loader's request reference
request
=
null
;
if
(
error
)
{
loader
.
error
=
{
status
:
req
.
status
,
message
:
'HLS playlist request error at URL: '
+
srcUrl
,
responseText
:
req
.
responseText
,
// MEDIA_ERR_NETWORK
code
:
2
};
return
loader
.
trigger
(
'error'
);
}
parser
=
new
m3u8
.
Parser
();
parser
.
push
(
req
.
responseText
);
parser
.
end
();
loader
.
state
=
'HAVE_MASTER'
;
parser
.
manifest
.
uri
=
srcUrl
;
// loaded a master playlist
if
(
parser
.
manifest
.
playlists
)
{
loader
.
master
=
parser
.
manifest
;
// setup by-URI lookups
i
=
loader
.
master
.
playlists
.
length
;
while
(
i
--
)
{
loader
.
master
.
playlists
[
loader
.
master
.
playlists
[
i
].
uri
]
=
loader
.
master
.
playlists
[
i
];
}
loader
.
trigger
(
'loadedplaylist'
);
if
(
!
request
)
{
// no media playlist was specifically selected so start
// from the first listed one
loader
.
media
(
parser
.
manifest
.
playlists
[
0
]);
}
return
;
}
// loaded a media playlist
// infer a master playlist if none was previously requested
loader
.
master
=
{
uri
:
window
.
location
.
href
,
playlists
:
[{
uri
:
srcUrl
}]
};
loader
.
master
.
playlists
[
srcUrl
]
=
loader
.
master
.
playlists
[
0
];
haveMetadata
(
req
,
srcUrl
);
return
loader
.
trigger
(
'loadedmetadata'
);
});
}
/**
* Update the PlaylistLoader state to reflect the changes in an
* update to the current media playlist.
* @param update {object} the updated media playlist object
*/
updateMediaPlaylist_
(
update
)
{
let
outdated
;
let
i
;
let
segment
;
outdated
=
this
.
media_
;
this
.
media_
=
this
.
master
.
playlists
[
update
.
uri
];
if
(
!
outdated
)
{
return
;
}
// don't track expired time until this flag is truthy
if
(
!
this
.
trackExpiredTime_
)
{
return
;
}
// if the update was the result of a rendition switch do not
// attempt to calculate expired_ since media-sequences need not
// correlate between renditions/variants
if
(
update
.
uri
!==
outdated
.
uri
)
{
return
;
}
// try using precise timing from first segment of the updated
// playlist
if
(
update
.
segments
.
length
)
{
if
(
update
.
segments
[
0
].
start
!==
undefined
)
{
this
.
expired_
=
update
.
segments
[
0
].
start
;
return
;
}
else
if
(
update
.
segments
[
0
].
end
!==
undefined
)
{
this
.
expired_
=
update
.
segments
[
0
].
end
-
update
.
segments
[
0
].
duration
;
return
;
}
}
// calculate expired by walking the outdated playlist
i
=
update
.
mediaSequence
-
outdated
.
mediaSequence
-
1
;
for
(;
i
>=
0
;
i
--
)
{
segment
=
outdated
.
segments
[
i
];
if
(
!
segment
)
{
// we missed information on this segment completely between
// playlist updates so we'll have to take an educated guess
// once we begin buffering again, any error we introduce can
// be corrected
this
.
expired_
+=
outdated
.
targetDuration
||
10
;
continue
;
}
if
(
segment
.
end
!==
undefined
)
{
this
.
expired_
=
segment
.
end
;
return
;
}
if
(
segment
.
start
!==
undefined
)
{
this
.
expired_
=
segment
.
start
+
segment
.
duration
;
return
;
}
this
.
expired_
+=
segment
.
duration
;
}
}
/**
* Determine the index of the segment that contains a specified
* playback position in the current media playlist. Early versions
* of the HLS specification require segment durations to be rounded
* to the nearest integer which means it may not be possible to
* determine the correct segment for a playback position if that
* position is within .5 seconds of the segment duration. This
* function will always return the lower of the two possible indices
* in those cases.
*
* @param time {number} The number of seconds since the earliest
* possible position to determine the containing segment for
* @returns {number} The number of the media segment that contains
* that time position. If the specified playback position is outside
* the time range of the current set of media segments, the return
* value will be clamped to the index of the segment containing the
* closest playback position that is currently available.
*/
getMediaIndexForTime_
(
time
)
{
let
i
;
* Update the PlaylistLoader state to reflect the changes in an
* update to the current media playlist.
* @param update {object} the updated media playlist object
*/
updateMediaPlaylist_
(
update
)
{
let
outdated
;
let
i
;
let
segment
;
outdated
=
this
.
media_
;
this
.
media_
=
this
.
master
.
playlists
[
update
.
uri
];
if
(
!
outdated
)
{
return
;
}
// don't track expired time until this flag is truthy
if
(
!
this
.
trackExpiredTime_
)
{
return
;
}
// if the update was the result of a rendition switch do not
// attempt to calculate expired_ since media-sequences need not
// correlate between renditions/variants
if
(
update
.
uri
!==
outdated
.
uri
)
{
return
;
}
// try using precise timing from first segment of the updated
// playlist
if
(
update
.
segments
.
length
)
{
if
(
update
.
segments
[
0
].
start
!==
undefined
)
{
this
.
expired_
=
update
.
segments
[
0
].
start
;
return
;
}
else
if
(
update
.
segments
[
0
].
end
!==
undefined
)
{
this
.
expired_
=
update
.
segments
[
0
].
end
-
update
.
segments
[
0
].
duration
;
return
;
}
}
// calculate expired by walking the outdated playlist
i
=
update
.
mediaSequence
-
outdated
.
mediaSequence
-
1
;
for
(;
i
>=
0
;
i
--
)
{
segment
=
outdated
.
segments
[
i
];
if
(
!
segment
)
{
// we missed information on this segment completely between
// playlist updates so we'll have to take an educated guess
// once we begin buffering again, any error we introduce can
// be corrected
this
.
expired_
+=
outdated
.
targetDuration
||
10
;
continue
;
}
if
(
segment
.
end
!==
undefined
)
{
this
.
expired_
=
segment
.
end
;
return
;
}
if
(
segment
.
start
!==
undefined
)
{
this
.
expired_
=
segment
.
start
+
segment
.
duration
;
return
;
}
this
.
expired_
+=
segment
.
duration
;
}
}
/**
* Determine the index of the segment that contains a specified
* playback position in the current media playlist. Early versions
* of the HLS specification require segment durations to be rounded
* to the nearest integer which means it may not be possible to
* determine the correct segment for a playback position if that
* position is within .5 seconds of the segment duration. This
* function will always return the lower of the two possible indices
* in those cases.
*
* @param time {number} The number of seconds since the earliest
* possible position to determine the containing segment for
* @returns {number} The number of the media segment that contains
* that time position. If the specified playback position is outside
* the time range of the current set of media segments, the return
* value will be clamped to the index of the segment containing the
* closest playback position that is currently available.
*/
getMediaIndexForTime_
(
time
)
{
let
i
;
let
segment
;
let
originalTime
=
time
;
let
numSegments
=
this
.
media_
.
segments
.
length
;
...
...
@@ -468,99 +468,99 @@ export default class PlaylistLoader extends Stream {
let
knownStart
;
let
knownEnd
;
if
(
!
this
.
media_
)
{
return
0
;
}
// when the requested position is earlier than the current set of
// segments, return the earliest segment index
if
(
time
<
0
)
{
return
0
;
}
// find segments with known timing information that bound the
// target time
for
(
i
=
0
;
i
<
numSegments
;
i
++
)
{
segment
=
this
.
media_
.
segments
[
i
];
if
(
segment
.
end
)
{
if
(
segment
.
end
>
time
)
{
knownEnd
=
segment
.
end
;
endIndex
=
i
;
break
;
}
else
{
knownStart
=
segment
.
end
;
startIndex
=
i
+
1
;
}
}
}
// use the bounds we just found and playlist information to
// estimate the segment that contains the time we are looking for
if
(
startIndex
!==
undefined
)
{
// We have a known-start point that is before our desired time so
// walk from that point forwards
time
=
time
-
knownStart
;
for
(
i
=
startIndex
;
i
<
(
endIndex
||
numSegments
);
i
++
)
{
segment
=
this
.
media_
.
segments
[
i
];
time
-=
segment
.
duration
;
if
(
time
<
0
)
{
return
i
;
}
}
if
(
i
>=
endIndex
)
{
// We haven't found a segment but we did hit a known end point
// so fallback to interpolating between the segment index
// based on the known span of the timeline we are dealing with
// and the number of segments inside that span
return
startIndex
+
Math
.
floor
(
((
originalTime
-
knownStart
)
/
(
knownEnd
-
knownStart
))
*
(
endIndex
-
startIndex
));
}
// We _still_ haven't found a segment so load the last one
return
lastSegment
;
}
else
if
(
endIndex
!==
undefined
)
{
// We _only_ have a known-end point that is after our desired time so
// walk from that point backwards
time
=
knownEnd
-
time
;
for
(
i
=
endIndex
;
i
>=
0
;
i
--
)
{
segment
=
this
.
media_
.
segments
[
i
];
time
-=
segment
.
duration
;
if
(
time
<
0
)
{
return
i
;
}
}
// We haven't found a segment so load the first one if time is zero
if
(
time
===
0
)
{
return
0
;
}
else
{
return
-
1
;
}
}
else
{
// We known nothing so walk from the front of the playlist,
// subtracting durations until we find a segment that contains
// time and return it
time
=
time
-
this
.
expired_
;
if
(
time
<
0
)
{
return
-
1
;
}
for
(
i
=
0
;
i
<
numSegments
;
i
++
)
{
segment
=
this
.
media_
.
segments
[
i
];
time
-=
segment
.
duration
;
if
(
time
<
0
)
{
return
i
;
}
}
// We are out of possible candidates so load the last one...
// The last one is the least likely to overlap a buffer and therefore
// the one most likely to tell us something about the timeline
return
lastSegment
;
}
}
if
(
!
this
.
media_
)
{
return
0
;
}
// when the requested position is earlier than the current set of
// segments, return the earliest segment index
if
(
time
<
0
)
{
return
0
;
}
// find segments with known timing information that bound the
// target time
for
(
i
=
0
;
i
<
numSegments
;
i
++
)
{
segment
=
this
.
media_
.
segments
[
i
];
if
(
segment
.
end
)
{
if
(
segment
.
end
>
time
)
{
knownEnd
=
segment
.
end
;
endIndex
=
i
;
break
;
}
else
{
knownStart
=
segment
.
end
;
startIndex
=
i
+
1
;
}
}
}
// use the bounds we just found and playlist information to
// estimate the segment that contains the time we are looking for
if
(
startIndex
!==
undefined
)
{
// We have a known-start point that is before our desired time so
// walk from that point forwards
time
=
time
-
knownStart
;
for
(
i
=
startIndex
;
i
<
(
endIndex
||
numSegments
);
i
++
)
{
segment
=
this
.
media_
.
segments
[
i
];
time
-=
segment
.
duration
;
if
(
time
<
0
)
{
return
i
;
}
}
if
(
i
>=
endIndex
)
{
// We haven't found a segment but we did hit a known end point
// so fallback to interpolating between the segment index
// based on the known span of the timeline we are dealing with
// and the number of segments inside that span
return
startIndex
+
Math
.
floor
(
((
originalTime
-
knownStart
)
/
(
knownEnd
-
knownStart
))
*
(
endIndex
-
startIndex
));
}
// We _still_ haven't found a segment so load the last one
return
lastSegment
;
}
else
if
(
endIndex
!==
undefined
)
{
// We _only_ have a known-end point that is after our desired time so
// walk from that point backwards
time
=
knownEnd
-
time
;
for
(
i
=
endIndex
;
i
>=
0
;
i
--
)
{
segment
=
this
.
media_
.
segments
[
i
];
time
-=
segment
.
duration
;
if
(
time
<
0
)
{
return
i
;
}
}
// We haven't found a segment so load the first one if time is zero
if
(
time
===
0
)
{
return
0
;
}
else
{
return
-
1
;
}
}
else
{
// We known nothing so walk from the front of the playlist,
// subtracting durations until we find a segment that contains
// time and return it
time
=
time
-
this
.
expired_
;
if
(
time
<
0
)
{
return
-
1
;
}
for
(
i
=
0
;
i
<
numSegments
;
i
++
)
{
segment
=
this
.
media_
.
segments
[
i
];
time
-=
segment
.
duration
;
if
(
time
<
0
)
{
return
i
;
}
}
// We are out of possible candidates so load the last one...
// The last one is the least likely to overlap a buffer and therefore
// the one most likely to tell us something about the timeline
return
lastSegment
;
}
}
}
...
...
Please
register
or
sign in
to post a comment