Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
SDK
/
exoplayer
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Settings
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
a7379ee6
authored
Jan 08, 2021
by
olly
Committed by
Oliver Woodman
Jan 08, 2021
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
PlayerEmsgHandler: Track stream max chunk times separately
Issue: #8408 PiperOrigin-RevId: 350786430
parent
c1529c46
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
70 additions
and
83 deletions
RELEASENOTES.md
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DefaultDashChunkSource.java
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/PlayerEmsgHandler.java
RELEASENOTES.md
View file @
a7379ee6
...
...
@@ -64,6 +64,9 @@
*
Support low-latency DASH playback (
`availabilityTimeOffset`
and
`ServiceDescription`
tags)
(
[
#4904
](
https://github.com/google/ExoPlayer/issues/4904
)
).
*
Improve logic for determining whether to refresh the manifest when a
chunk load error occurs in a live streams that contains EMSG data
(
[
#8408
](
https://github.com/google/ExoPlayer/issues/8408
)
).
*
HLS:
*
Support playlist delta updates, blocking playlist reloads and rendition
reports.
...
...
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DefaultDashChunkSource.java
View file @
a7379ee6
...
...
@@ -429,8 +429,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
if
(!
cancelable
)
{
return
false
;
}
if
(
playerTrackEmsgHandler
!=
null
&&
playerTrackEmsgHandler
.
maybeRefreshManifestOnLoadingError
(
chunk
))
{
if
(
playerTrackEmsgHandler
!=
null
&&
playerTrackEmsgHandler
.
onChunkLoadError
(
chunk
))
{
return
true
;
}
// Workaround for missing segment at the end of the period
...
...
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/PlayerEmsgHandler.java
View file @
a7379ee6
...
...
@@ -85,8 +85,7 @@ public final class PlayerEmsgHandler implements Handler.Callback {
private
DashManifest
manifest
;
private
long
expiredManifestPublishTimeUs
;
private
long
lastLoadedChunkEndTimeUs
;
private
long
lastLoadedChunkEndTimeBeforeRefreshUs
;
private
boolean
chunkLoadedCompletedSinceLastManifestRefreshRequest
;
private
boolean
isWaitingForManifestRefresh
;
private
boolean
released
;
...
...
@@ -105,8 +104,6 @@ public final class PlayerEmsgHandler implements Handler.Callback {
manifestPublishTimeToExpiryTimeUs
=
new
TreeMap
<>();
handler
=
Util
.
createHandlerForCurrentLooper
(
/* callback= */
this
);
decoder
=
new
EventMessageDecoder
();
lastLoadedChunkEndTimeUs
=
C
.
TIME_UNSET
;
lastLoadedChunkEndTimeBeforeRefreshUs
=
C
.
TIME_UNSET
;
}
/**
...
...
@@ -121,6 +118,36 @@ public final class PlayerEmsgHandler implements Handler.Callback {
removePreviouslyExpiredManifestPublishTimeValues
();
}
/** Returns a {@link TrackOutput} that emsg messages could be written to. */
public
PlayerTrackEmsgHandler
newPlayerTrackEmsgHandler
()
{
return
new
PlayerTrackEmsgHandler
(
allocator
);
}
/** Release this emsg handler. It should not be reused after this call. */
public
void
release
()
{
released
=
true
;
handler
.
removeCallbacksAndMessages
(
null
);
}
@Override
public
boolean
handleMessage
(
Message
message
)
{
if
(
released
)
{
return
true
;
}
switch
(
message
.
what
)
{
case
(
EMSG_MANIFEST_EXPIRED
):
ManifestExpiryEventInfo
messageObj
=
(
ManifestExpiryEventInfo
)
message
.
obj
;
handleManifestExpiredMessage
(
messageObj
.
eventTimeUs
,
messageObj
.
manifestPublishTimeMsInEmsg
);
return
true
;
default
:
// Do nothing.
}
return
false
;
}
// Internal methods.
/* package */
boolean
maybeRefreshManifestBeforeLoadingNextChunk
(
long
presentationPositionUs
)
{
if
(!
manifest
.
dynamic
)
{
return
false
;
...
...
@@ -146,83 +173,27 @@ public final class PlayerEmsgHandler implements Handler.Callback {
return
manifestRefreshNeeded
;
}
/**
* For live streaming with emsg event stream, forward seeking can seek pass the emsg messages that
* signals end-of-stream or Manifest expiry, which results in load error. In this case, we should
* notify the Dash media source to refresh its manifest.
*
* @param chunk The chunk whose load encountered the error.
* @return True if manifest refresh has been requested, false otherwise.
*/
/* package */
boolean
maybeRefreshManifestOnLoadingError
(
Chunk
chunk
)
{
/* package */
void
onChunkLoadCompleted
(
Chunk
chunk
)
{
chunkLoadedCompletedSinceLastManifestRefreshRequest
=
true
;
}
/* package */
boolean
onChunkLoadError
(
boolean
isForwardSeek
)
{
if
(!
manifest
.
dynamic
)
{
return
false
;
}
if
(
isWaitingForManifestRefresh
)
{
return
true
;
}
boolean
isAfterForwardSeek
=
lastLoadedChunkEndTimeUs
!=
C
.
TIME_UNSET
&&
lastLoadedChunkEndTimeUs
<
chunk
.
startTimeUs
;
if
(
isAfterForwardSeek
)
{
// if we are after a forward seek, and the playback is dynamic with embedded emsg stream,
// there's a chance that we have seek over the emsg messages, in which case we should ask
// media source for a refresh.
if
(
isForwardSeek
)
{
// If a forward seek has occurred, there's a chance that the seek has skipped EMSGs signalling
// end-of-stream or manifest expiration. We must assume that the manifest might need to be
// refreshed.
maybeNotifyDashManifestRefreshNeeded
();
return
true
;
}
return
false
;
}
/**
* Called when the a new chunk in the current media stream has been loaded.
*
* @param chunk The chunk whose load has been completed.
*/
/* package */
void
onChunkLoadCompleted
(
Chunk
chunk
)
{
if
(
lastLoadedChunkEndTimeUs
!=
C
.
TIME_UNSET
||
chunk
.
endTimeUs
>
lastLoadedChunkEndTimeUs
)
{
lastLoadedChunkEndTimeUs
=
chunk
.
endTimeUs
;
}
}
/**
* Returns whether an event with given schemeIdUri and value is a DASH emsg event targeting the
* player.
*/
public
static
boolean
isPlayerEmsgEvent
(
String
schemeIdUri
,
String
value
)
{
return
"urn:mpeg:dash:event:2012"
.
equals
(
schemeIdUri
)
&&
(
"1"
.
equals
(
value
)
||
"2"
.
equals
(
value
)
||
"3"
.
equals
(
value
));
}
/** Returns a {@link TrackOutput} that emsg messages could be written to. */
public
PlayerTrackEmsgHandler
newPlayerTrackEmsgHandler
()
{
return
new
PlayerTrackEmsgHandler
(
allocator
);
}
/** Release this emsg handler. It should not be reused after this call. */
public
void
release
()
{
released
=
true
;
handler
.
removeCallbacksAndMessages
(
null
);
}
@Override
public
boolean
handleMessage
(
Message
message
)
{
if
(
released
)
{
return
true
;
}
switch
(
message
.
what
)
{
case
(
EMSG_MANIFEST_EXPIRED
):
ManifestExpiryEventInfo
messageObj
=
(
ManifestExpiryEventInfo
)
message
.
obj
;
handleManifestExpiredMessage
(
messageObj
.
eventTimeUs
,
messageObj
.
manifestPublishTimeMsInEmsg
);
return
true
;
default
:
// Do nothing.
}
return
false
;
}
// Internal methods.
private
void
handleManifestExpiredMessage
(
long
eventTimeUs
,
long
manifestPublishTimeMsInEmsg
)
{
Long
previousExpiryTimeUs
=
manifestPublishTimeToExpiryTimeUs
.
get
(
manifestPublishTimeMsInEmsg
);
if
(
previousExpiryTimeUs
==
null
)
{
...
...
@@ -256,13 +227,12 @@ public final class PlayerEmsgHandler implements Handler.Callback {
/** Requests DASH media manifest to be refreshed if necessary. */
private
void
maybeNotifyDashManifestRefreshNeeded
()
{
if
(
lastLoadedChunkEndTimeBeforeRefreshUs
!=
C
.
TIME_UNSET
&&
lastLoadedChunkEndTimeBeforeRefreshUs
==
lastLoadedChunkEndTimeUs
)
{
// Already requested manifest refresh.
if
(!
chunkLoadedCompletedSinceLastManifestRefreshRequest
)
{
// Don't request a refresh unless some progress has been made.
return
;
}
isWaitingForManifestRefresh
=
true
;
lastLoadedChunkEndTimeBeforeRefreshUs
=
lastLoadedChunkEndTimeUs
;
chunkLoadedCompletedSinceLastManifestRefreshRequest
=
false
;
playerEmsgCallback
.
onDashManifestRefreshRequested
();
}
...
...
@@ -275,6 +245,15 @@ public final class PlayerEmsgHandler implements Handler.Callback {
}
}
/**
* Returns whether an event with given schemeIdUri and value is a DASH emsg event targeting the
* player.
*/
private
static
boolean
isPlayerEmsgEvent
(
String
schemeIdUri
,
String
value
)
{
return
"urn:mpeg:dash:event:2012"
.
equals
(
schemeIdUri
)
&&
(
"1"
.
equals
(
value
)
||
"2"
.
equals
(
value
)
||
"3"
.
equals
(
value
));
}
/** Handles emsg messages for a specific track for the player. */
public
final
class
PlayerTrackEmsgHandler
implements
TrackOutput
{
...
...
@@ -282,10 +261,13 @@ public final class PlayerEmsgHandler implements Handler.Callback {
private
final
FormatHolder
formatHolder
;
private
final
MetadataInputBuffer
buffer
;
private
long
maxLoadedChunkEndTimeUs
;
/* package */
PlayerTrackEmsgHandler
(
Allocator
allocator
)
{
this
.
sampleQueue
=
SampleQueue
.
createWithoutDrm
(
allocator
);
formatHolder
=
new
FormatHolder
();
buffer
=
new
MetadataInputBuffer
();
maxLoadedChunkEndTimeUs
=
C
.
TIME_UNSET
;
}
@Override
...
...
@@ -325,24 +307,27 @@ public final class PlayerEmsgHandler implements Handler.Callback {
}
/**
* Called when
the a new chunk in the current media stream has been load
ed.
* Called when
a chunk load has been complet
ed.
*
* @param chunk The chunk whose load has been completed.
*/
public
void
onChunkLoadCompleted
(
Chunk
chunk
)
{
if
(
maxLoadedChunkEndTimeUs
==
C
.
TIME_UNSET
||
chunk
.
endTimeUs
>
maxLoadedChunkEndTimeUs
)
{
maxLoadedChunkEndTimeUs
=
chunk
.
endTimeUs
;
}
PlayerEmsgHandler
.
this
.
onChunkLoadCompleted
(
chunk
);
}
/**
* For live streaming with emsg event stream, forward seeking can seek pass the emsg messages
* that signals end-of-stream or Manifest expiry, which results in load error. In this case, we
* should notify the Dash media source to refresh its manifest.
* Called when a chunk load has encountered an error.
*
* @param chunk The chunk whose load encountered
the
error.
* @return
True if manifest refresh has been requested, false otherwise
.
* @param chunk The chunk whose load encountered
an
error.
* @return
Whether a manifest refresh has been requested
.
*/
public
boolean
maybeRefreshManifestOnLoadingError
(
Chunk
chunk
)
{
return
PlayerEmsgHandler
.
this
.
maybeRefreshManifestOnLoadingError
(
chunk
);
public
boolean
onChunkLoadError
(
Chunk
chunk
)
{
boolean
isAfterForwardSeek
=
maxLoadedChunkEndTimeUs
!=
C
.
TIME_UNSET
&&
maxLoadedChunkEndTimeUs
<
chunk
.
startTimeUs
;
return
PlayerEmsgHandler
.
this
.
onChunkLoadError
(
isAfterForwardSeek
);
}
/** Release this track emsg handler. It should not be reused after this call. */
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment