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
8991586c
authored
Apr 06, 2020
by
Oliver Woodman
Browse files
Options
_('Browse Files')
Download
Plain Diff
Merge pull request #7034 from TiVo:p-exception-unreported-discontinuity
PiperOrigin-RevId: 305006564
parents
c13f41af
48592071
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
150 additions
and
25 deletions
library/core/src/main/java/com/google/android/exoplayer2/source/SampleQueue.java
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/UnexpectedSampleTimestampException.java
library/core/src/main/java/com/google/android/exoplayer2/source/SampleQueue.java
View file @
8991586c
...
...
@@ -488,7 +488,7 @@ public class SampleQueue implements TrackOutput {
}
@Override
public
final
void
sampleMetadata
(
public
void
sampleMetadata
(
long
timeUs
,
@C
.
BufferFlags
int
flags
,
int
size
,
...
...
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java
View file @
8991586c
...
...
@@ -129,7 +129,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
private
final
ArrayList
<
HlsSampleStream
>
hlsSampleStreams
;
private
final
Map
<
String
,
DrmInitData
>
overridingDrmInitData
;
private
FormatAdjusting
SampleQueue
[]
sampleQueues
;
private
Hls
SampleQueue
[]
sampleQueues
;
private
int
[]
sampleQueueTrackIds
;
private
Set
<
Integer
>
sampleQueueMappingDoneByType
;
private
SparseIntArray
sampleQueueIndicesByType
;
...
...
@@ -164,7 +164,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
private
boolean
tracksEnded
;
private
long
sampleOffsetUs
;
@Nullable
private
DrmInitData
drmInitData
;
private
int
sourceId
;
@Nullable
private
HlsMediaChunk
sourceChunk
;
/**
* @param trackType The type of the track. One of the {@link C} {@code TRACK_TYPE_*} constants.
...
...
@@ -209,7 +209,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
sampleQueueTrackIds
=
new
int
[
0
];
sampleQueueMappingDoneByType
=
new
HashSet
<>(
MAPPABLE_TYPES
.
size
());
sampleQueueIndicesByType
=
new
SparseIntArray
(
MAPPABLE_TYPES
.
size
());
sampleQueues
=
new
FormatAdjusting
SampleQueue
[
0
];
sampleQueues
=
new
Hls
SampleQueue
[
0
];
sampleQueueIsAudioVideoFlags
=
new
boolean
[
0
];
sampleQueuesEnabledStates
=
new
boolean
[
0
];
mediaChunks
=
new
ArrayList
<>();
...
...
@@ -817,19 +817,19 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
/**
* Performs initialization for a media chunk that's about to start loading.
*
* @param
mediaC
hunk The media chunk that's about to start loading.
* @param
c
hunk The media chunk that's about to start loading.
*/
private
void
initMediaChunkLoad
(
HlsMediaChunk
mediaC
hunk
)
{
source
Id
=
mediaChunk
.
uid
;
upstreamTrackFormat
=
mediaC
hunk
.
trackFormat
;
private
void
initMediaChunkLoad
(
HlsMediaChunk
c
hunk
)
{
source
Chunk
=
chunk
;
upstreamTrackFormat
=
c
hunk
.
trackFormat
;
pendingResetPositionUs
=
C
.
TIME_UNSET
;
mediaChunks
.
add
(
mediaC
hunk
);
mediaChunks
.
add
(
c
hunk
);
mediaC
hunk
.
init
(
this
);
for
(
SampleQueue
sampleQueue
:
sampleQueues
)
{
sampleQueue
.
s
ourceId
(
sourceId
);
c
hunk
.
init
(
this
);
for
(
Hls
SampleQueue
sampleQueue
:
sampleQueues
)
{
sampleQueue
.
s
etSourceChunk
(
chunk
);
}
if
(
mediaC
hunk
.
shouldSpliceIn
)
{
if
(
c
hunk
.
shouldSpliceIn
)
{
for
(
SampleQueue
sampleQueue
:
sampleQueues
)
{
sampleQueue
.
splice
();
}
...
...
@@ -906,18 +906,19 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
int
trackCount
=
sampleQueues
.
length
;
boolean
isAudioVideo
=
type
==
C
.
TRACK_TYPE_AUDIO
||
type
==
C
.
TRACK_TYPE_VIDEO
;
FormatAdjustingSampleQueue
trackOutput
=
new
FormatAdjustingSampleQueue
(
allocator
,
drmSessionManager
,
eventDispatcher
,
overridingDrmInitData
);
HlsSampleQueue
sampleQueue
=
new
HlsSampleQueue
(
allocator
,
drmSessionManager
,
eventDispatcher
,
overridingDrmInitData
);
if
(
isAudioVideo
)
{
trackOutput
.
setDrmInitData
(
drmInitData
);
sampleQueue
.
setDrmInitData
(
drmInitData
);
}
sampleQueue
.
setSampleOffsetUs
(
sampleOffsetUs
);
if
(
sourceChunk
!=
null
)
{
sampleQueue
.
setSourceChunk
(
sourceChunk
);
}
trackOutput
.
setSampleOffsetUs
(
sampleOffsetUs
);
trackOutput
.
sourceId
(
sourceId
);
trackOutput
.
setUpstreamFormatChangeListener
(
this
);
sampleQueue
.
setUpstreamFormatChangeListener
(
this
);
sampleQueueTrackIds
=
Arrays
.
copyOf
(
sampleQueueTrackIds
,
trackCount
+
1
);
sampleQueueTrackIds
[
trackCount
]
=
id
;
sampleQueues
=
Util
.
nullSafeArrayAppend
(
sampleQueues
,
trackOutput
);
sampleQueues
=
Util
.
nullSafeArrayAppend
(
sampleQueues
,
sampleQueue
);
sampleQueueIsAudioVideoFlags
=
Arrays
.
copyOf
(
sampleQueueIsAudioVideoFlags
,
trackCount
+
1
);
sampleQueueIsAudioVideoFlags
[
trackCount
]
=
isAudioVideo
;
haveAudioVideoSampleQueues
|=
sampleQueueIsAudioVideoFlags
[
trackCount
];
...
...
@@ -928,7 +929,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
primarySampleQueueType
=
type
;
}
sampleQueuesEnabledStates
=
Arrays
.
copyOf
(
sampleQueuesEnabledStates
,
trackCount
+
1
);
return
trackOutput
;
return
sampleQueue
;
}
@Override
...
...
@@ -1341,12 +1342,40 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
return
new
DummyTrackOutput
();
}
private
static
final
class
FormatAdjustingSampleQueue
extends
SampleQueue
{
/**
* A {@link SampleQueue} that adds HLS specific functionality:
*
* <ul>
* <li>Detection of spurious discontinuities, by checking sample timestamps against the range
* expected for the currently loading chunk.
* <li>Stripping private timestamp metadata from {@link Format Formats} to avoid an excessive
* number of format switches in the queue.
* <li>Overriding of {@link Format#drmInitData}.
* </ul>
*/
private
static
final
class
HlsSampleQueue
extends
SampleQueue
{
/**
* The fraction of the chunk duration from which timestamps of samples loaded from within a
* chunk are allowed to deviate from the expected range.
*/
private
static
final
double
MAX_TIMESTAMP_DEVIATION_FRACTION
=
0.5
;
/**
* A minimum tolerance for sample timestamps in microseconds. Timestamps of samples loaded from
* within a chunk are always allowed to deviate up to this amount from the expected range.
*/
private
static
final
long
MIN_TIMESTAMP_DEVIATION_TOLERANCE_US
=
4_000_000
;
@Nullable
private
HlsMediaChunk
sourceChunk
;
private
long
sourceChunkLastSampleTimeUs
;
private
long
minAllowedSampleTimeUs
;
private
long
maxAllowedSampleTimeUs
;
private
final
Map
<
String
,
DrmInitData
>
overridingDrmInitData
;
@Nullable
private
DrmInitData
drmInitData
;
p
ublic
FormatAdjusting
SampleQueue
(
p
rivate
Hls
SampleQueue
(
Allocator
allocator
,
DrmSessionManager
drmSessionManager
,
MediaSourceEventDispatcher
eventDispatcher
,
...
...
@@ -1355,6 +1384,19 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
this
.
overridingDrmInitData
=
overridingDrmInitData
;
}
public
void
setSourceChunk
(
HlsMediaChunk
chunk
)
{
sourceChunk
=
chunk
;
sourceChunkLastSampleTimeUs
=
C
.
TIME_UNSET
;
sourceId
(
chunk
.
uid
);
long
allowedDeviationUs
=
Math
.
max
(
(
long
)
((
chunk
.
endTimeUs
-
chunk
.
startTimeUs
)
*
MAX_TIMESTAMP_DEVIATION_FRACTION
),
MIN_TIMESTAMP_DEVIATION_TOLERANCE_US
);
minAllowedSampleTimeUs
=
chunk
.
startTimeUs
-
allowedDeviationUs
;
maxAllowedSampleTimeUs
=
chunk
.
endTimeUs
+
allowedDeviationUs
;
}
public
void
setDrmInitData
(
@Nullable
DrmInitData
drmInitData
)
{
this
.
drmInitData
=
drmInitData
;
invalidateUpstreamFormatAdjustment
();
...
...
@@ -1415,13 +1457,31 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
}
return
new
Metadata
(
newMetadataEntries
);
}
@Override
public
void
sampleMetadata
(
long
timeUs
,
@C
.
BufferFlags
int
flags
,
int
size
,
int
offset
,
@Nullable
CryptoData
cryptoData
)
{
// TODO: Uncomment this to reject samples with unexpected timestamps. See
// https://github.com/google/ExoPlayer/issues/7030.
// if (timeUs < minAllowedSampleTimeUs || timeUs > maxAllowedSampleTimeUs) {
// Util.sneakyThrow(
// new UnexpectedSampleTimestampException(
// sourceChunk, sourceChunkLastSampleTimeUs, timeUs));
// }
sourceChunkLastSampleTimeUs
=
timeUs
;
super
.
sampleMetadata
(
timeUs
,
flags
,
size
,
offset
,
cryptoData
);
}
}
private
static
class
EmsgUnwrappingTrackOutput
implements
TrackOutput
{
private
static
final
String
TAG
=
"EmsgUnwrappingTrackOutput"
;
// TODO
(ibaker)
: Create a Formats util class with common constants like this.
// TODO: Create a Formats util class with common constants like this.
private
static
final
Format
ID3_FORMAT
=
new
Format
.
Builder
().
setSampleMimeType
(
MimeTypes
.
APPLICATION_ID3
).
build
();
private
static
final
Format
EMSG_FORMAT
=
...
...
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/UnexpectedSampleTimestampException.java
0 → 100644
View file @
8991586c
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
com
.
google
.
android
.
exoplayer2
.
source
.
hls
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.source.SampleQueue
;
import
com.google.android.exoplayer2.source.chunk.MediaChunk
;
import
java.io.IOException
;
/**
* Thrown when an attempt is made to write a sample to a {@link SampleQueue} whose timestamp is
* inconsistent with the chunk from which it originates.
*/
/* package */
final
class
UnexpectedSampleTimestampException
extends
IOException
{
/** The {@link MediaChunk} that contained the rejected sample. */
public
final
MediaChunk
mediaChunk
;
/**
* The timestamp of the last sample that was loaded from {@link #mediaChunk} and successfully
* written to the {@link SampleQueue}, in microseconds. {@link C#TIME_UNSET} if the first sample
* in the chunk was rejected.
*/
public
final
long
lastAcceptedSampleTimeUs
;
/** The timestamp of the rejected sample, in microseconds. */
public
final
long
rejectedSampleTimeUs
;
/**
* Constructs an instance.
*
* @param mediaChunk The {@link MediaChunk} with the unexpected sample timestamp.
* @param lastAcceptedSampleTimeUs The timestamp of the last sample that was loaded from the chunk
* and successfully written to the {@link SampleQueue}, in microseconds. {@link C#TIME_UNSET}
* if the first sample in the chunk was rejected.
* @param rejectedSampleTimeUs The timestamp of the rejected sample, in microseconds.
*/
public
UnexpectedSampleTimestampException
(
MediaChunk
mediaChunk
,
long
lastAcceptedSampleTimeUs
,
long
rejectedSampleTimeUs
)
{
super
(
"Unexpected sample timestamp: "
+
C
.
usToMs
(
rejectedSampleTimeUs
)
+
" in chunk ["
+
mediaChunk
.
startTimeUs
+
", "
+
mediaChunk
.
endTimeUs
+
"]"
);
this
.
mediaChunk
=
mediaChunk
;
this
.
lastAcceptedSampleTimeUs
=
lastAcceptedSampleTimeUs
;
this
.
rejectedSampleTimeUs
=
rejectedSampleTimeUs
;
}
}
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