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
f8d60e2b
authored
Sep 13, 2021
by
olly
Committed by
Christos Tsilopoulos
Sep 14, 2021
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Add flag for constant bitrate seeking even if input length is unknown
PiperOrigin-RevId: 396363113
parent
cf0ec919
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
184 additions
and
34 deletions
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/ConstantBitrateSeekMap.java
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/DefaultExtractorsFactory.java
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/amr/AmrExtractor.java
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp3/ConstantBitrateSeeker.java
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp3/Mp3Extractor.java
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/ts/AdtsExtractor.java
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/ConstantBitrateSeekMap.java
View file @
f8d60e2b
...
...
@@ -16,9 +16,9 @@
package
com
.
google
.
android
.
exoplayer2
.
extractor
;
import
static
java
.
lang
.
Math
.
max
;
import
static
java
.
lang
.
Math
.
min
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.util.Util
;
/**
* A {@link SeekMap} implementation that assumes the stream has a constant bitrate and consists of
...
...
@@ -33,9 +33,10 @@ public class ConstantBitrateSeekMap implements SeekMap {
private
final
long
dataSize
;
private
final
int
bitrate
;
private
final
long
durationUs
;
private
final
boolean
allowSeeksIfLengthUnknown
;
/**
* C
onstructs a new instance from a stream
.
* C
reates an instance with {@code allowSeeksIfLengthUnknown} set to {@code false}
.
*
* @param inputLength The length of the stream in bytes, or {@link C#LENGTH_UNSET} if unknown.
* @param firstFrameBytePosition The byte-position of the first frame in the stream.
...
...
@@ -45,10 +46,36 @@ public class ConstantBitrateSeekMap implements SeekMap {
*/
public
ConstantBitrateSeekMap
(
long
inputLength
,
long
firstFrameBytePosition
,
int
bitrate
,
int
frameSize
)
{
this
(
inputLength
,
firstFrameBytePosition
,
bitrate
,
frameSize
,
/* allowSeeksIfLengthUnknown= */
false
);
}
/**
* Creates an instance.
*
* @param inputLength The length of the stream in bytes, or {@link C#LENGTH_UNSET} if unknown.
* @param firstFrameBytePosition The byte-position of the first frame in the stream.
* @param bitrate The bitrate (which is assumed to be constant in the stream).
* @param frameSize The size of each frame in the stream in bytes. May be {@link C#LENGTH_UNSET}
* if unknown.
* @param allowSeeksIfLengthUnknown Whether to allow seeking even if the length of the content is
* unknown.
*/
public
ConstantBitrateSeekMap
(
long
inputLength
,
long
firstFrameBytePosition
,
int
bitrate
,
int
frameSize
,
boolean
allowSeeksIfLengthUnknown
)
{
this
.
inputLength
=
inputLength
;
this
.
firstFrameBytePosition
=
firstFrameBytePosition
;
this
.
frameSize
=
frameSize
==
C
.
LENGTH_UNSET
?
1
:
frameSize
;
this
.
bitrate
=
bitrate
;
this
.
allowSeeksIfLengthUnknown
=
allowSeeksIfLengthUnknown
;
if
(
inputLength
==
C
.
LENGTH_UNSET
)
{
dataSize
=
C
.
LENGTH_UNSET
;
...
...
@@ -61,18 +88,23 @@ public class ConstantBitrateSeekMap implements SeekMap {
@Override
public
boolean
isSeekable
()
{
return
dataSize
!=
C
.
LENGTH_UNSET
;
return
dataSize
!=
C
.
LENGTH_UNSET
||
allowSeeksIfLengthUnknown
;
}
@Override
public
SeekPoints
getSeekPoints
(
long
timeUs
)
{
if
(
dataSize
==
C
.
LENGTH_UNSET
)
{
if
(
dataSize
==
C
.
LENGTH_UNSET
&&
!
allowSeeksIfLengthUnknown
)
{
return
new
SeekPoints
(
new
SeekPoint
(
0
,
firstFrameBytePosition
));
}
long
seekFramePosition
=
getFramePositionForTimeUs
(
timeUs
);
long
seekTimeUs
=
getTimeUsAtPosition
(
seekFramePosition
);
SeekPoint
seekPoint
=
new
SeekPoint
(
seekTimeUs
,
seekFramePosition
);
if
(
seekTimeUs
>=
timeUs
||
seekFramePosition
+
frameSize
>=
inputLength
)
{
// We only return a single seek point if the length is unknown, to avoid generating a second
// seek point beyond the end of the data in the case that the requested seek position is valid,
// but very close to the end of the content.
if
(
dataSize
==
C
.
LENGTH_UNSET
||
seekTimeUs
>=
timeUs
||
seekFramePosition
+
frameSize
>=
inputLength
)
{
return
new
SeekPoints
(
seekPoint
);
}
else
{
long
secondSeekPosition
=
seekFramePosition
+
frameSize
;
...
...
@@ -118,8 +150,10 @@ public class ConstantBitrateSeekMap implements SeekMap {
long
positionOffset
=
(
timeUs
*
bitrate
)
/
(
C
.
MICROS_PER_SECOND
*
C
.
BITS_PER_BYTE
);
// Constrain to nearest preceding frame offset.
positionOffset
=
(
positionOffset
/
frameSize
)
*
frameSize
;
positionOffset
=
Util
.
constrainValue
(
positionOffset
,
/* min= */
0
,
/* max= */
dataSize
-
frameSize
);
if
(
dataSize
!=
C
.
LENGTH_UNSET
)
{
positionOffset
=
min
(
positionOffset
,
dataSize
-
frameSize
);
}
positionOffset
=
max
(
positionOffset
,
0
);
return
firstFrameBytePosition
+
positionOffset
;
}
}
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/DefaultExtractorsFactory.java
View file @
f8d60e2b
...
...
@@ -20,6 +20,8 @@ import static com.google.android.exoplayer2.util.FileTypes.inferFileTypeFromUri;
import
android.net.Uri
;
import
androidx.annotation.Nullable
;
import
com.google.android.exoplayer2.PlaybackException
;
import
com.google.android.exoplayer2.Player
;
import
com.google.android.exoplayer2.extractor.amr.AmrExtractor
;
import
com.google.android.exoplayer2.extractor.flac.FlacExtractor
;
import
com.google.android.exoplayer2.extractor.flv.FlvExtractor
;
...
...
@@ -125,6 +127,7 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory {
}
private
boolean
constantBitrateSeekingEnabled
;
private
boolean
constantBitrateSeekingAlwaysEnabled
;
@AdtsExtractor
.
Flags
private
int
adtsFlags
;
@AmrExtractor
.
Flags
private
int
amrFlags
;
@FlacExtractor
.
Flags
private
int
flacFlags
;
...
...
@@ -159,6 +162,30 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory {
}
/**
* Convenience method to set whether approximate seeking using constant bitrate assumptions should
* be enabled for all extractors that support it, and if it should be enabled even if the content
* length (and hence the duration of the media) is unknown. If set to true, the flags required to
* enable this functionality will be OR'd with those passed to the setters when creating extractor
* instances. If set to false then the flags passed to the setters will be used without
* modification.
*
* <p>When seeking into content where the length is unknown, application code should ensure that
* requested seek positions are valid, or should be ready to handle playback failures reported
* through {@link Player.Listener#onPlayerError} with {@link PlaybackException#errorCode} set to
* {@link PlaybackException#ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE}.
*
* @param constantBitrateSeekingAlwaysEnabled Whether approximate seeking using a constant bitrate
* assumption should be enabled for all extractors that support it, including when the content
* duration is unknown.
* @return The factory, for convenience.
*/
public
synchronized
DefaultExtractorsFactory
setConstantBitrateSeekingAlwaysEnabled
(
boolean
constantBitrateSeekingAlwaysEnabled
)
{
this
.
constantBitrateSeekingAlwaysEnabled
=
constantBitrateSeekingAlwaysEnabled
;
return
this
;
}
/**
* Sets flags for {@link AdtsExtractor} instances created by the factory.
*
* @see AdtsExtractor#AdtsExtractor(int)
...
...
@@ -333,6 +360,9 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory {
adtsFlags
|
(
constantBitrateSeekingEnabled
?
AdtsExtractor
.
FLAG_ENABLE_CONSTANT_BITRATE_SEEKING
:
0
)
|
(
constantBitrateSeekingAlwaysEnabled
?
AdtsExtractor
.
FLAG_ENABLE_CONSTANT_BITRATE_SEEKING_ALWAYS
:
0
)));
break
;
case
FileTypes
.
AMR
:
...
...
@@ -341,6 +371,9 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory {
amrFlags
|
(
constantBitrateSeekingEnabled
?
AmrExtractor
.
FLAG_ENABLE_CONSTANT_BITRATE_SEEKING
:
0
)
|
(
constantBitrateSeekingAlwaysEnabled
?
AmrExtractor
.
FLAG_ENABLE_CONSTANT_BITRATE_SEEKING_ALWAYS
:
0
)));
break
;
case
FileTypes
.
FLAC
:
...
...
@@ -367,6 +400,9 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory {
mp3Flags
|
(
constantBitrateSeekingEnabled
?
Mp3Extractor
.
FLAG_ENABLE_CONSTANT_BITRATE_SEEKING
:
0
)
|
(
constantBitrateSeekingAlwaysEnabled
?
Mp3Extractor
.
FLAG_ENABLE_CONSTANT_BITRATE_SEEKING_ALWAYS
:
0
)));
break
;
case
FileTypes
.
MP4
:
...
...
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/amr/AmrExtractor.java
View file @
f8d60e2b
...
...
@@ -19,6 +19,8 @@ import androidx.annotation.IntDef;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.ParserException
;
import
com.google.android.exoplayer2.PlaybackException
;
import
com.google.android.exoplayer2.Player
;
import
com.google.android.exoplayer2.extractor.ConstantBitrateSeekMap
;
import
com.google.android.exoplayer2.extractor.Extractor
;
import
com.google.android.exoplayer2.extractor.ExtractorInput
;
...
...
@@ -52,20 +54,33 @@ public final class AmrExtractor implements Extractor {
public
static
final
ExtractorsFactory
FACTORY
=
()
->
new
Extractor
[]
{
new
AmrExtractor
()};
/**
* Flags controlling the behavior of the extractor. Possible flag value is {@link
* #FLAG_ENABLE_CONSTANT_BITRATE_SEEKING}.
* Flags controlling the behavior of the extractor. Possible flag values are {@link
* #FLAG_ENABLE_CONSTANT_BITRATE_SEEKING} and {@link
* #FLAG_ENABLE_CONSTANT_BITRATE_SEEKING_ALWAYS}.
*/
@Documented
@Retention
(
RetentionPolicy
.
SOURCE
)
@IntDef
(
flag
=
true
,
value
=
{
FLAG_ENABLE_CONSTANT_BITRATE_SEEKING
})
value
=
{
FLAG_ENABLE_CONSTANT_BITRATE_SEEKING
,
FLAG_ENABLE_CONSTANT_BITRATE_SEEKING_ALWAYS
})
public
@interface
Flags
{}
/**
* Flag to force enable seeking using a constant bitrate assumption in cases where seeking would
* otherwise not be possible.
*/
public
static
final
int
FLAG_ENABLE_CONSTANT_BITRATE_SEEKING
=
1
;
/**
* Like {@link #FLAG_ENABLE_CONSTANT_BITRATE_SEEKING}, except that seeking is also enabled in
* cases where the content length (and hence the duration of the media) is unknown. Application
* code should ensure that requested seek positions are valid when using this flag, or be ready to
* handle playback failures reported through {@link Player.Listener#onPlayerError} with {@link
* PlaybackException#errorCode} set to {@link
* PlaybackException#ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE}.
*
* <p>If this flag is set, then the behavior enabled by {@link
* #FLAG_ENABLE_CONSTANT_BITRATE_SEEKING} is implicitly enabled as well.
*/
public
static
final
int
FLAG_ENABLE_CONSTANT_BITRATE_SEEKING_ALWAYS
=
1
<<
1
;
/**
* The frame size in bytes, including header (1 byte), for each of the 16 frame types for AMR
...
...
@@ -152,6 +167,9 @@ public final class AmrExtractor implements Extractor {
/** @param flags Flags that control the extractor's behavior. */
public
AmrExtractor
(
@Flags
int
flags
)
{
if
((
flags
&
FLAG_ENABLE_CONSTANT_BITRATE_SEEKING_ALWAYS
)
!=
0
)
{
flags
|=
FLAG_ENABLE_CONSTANT_BITRATE_SEEKING
;
}
this
.
flags
=
flags
;
scratch
=
new
byte
[
1
];
firstSampleSize
=
C
.
LENGTH_UNSET
;
...
...
@@ -360,15 +378,18 @@ public final class AmrExtractor implements Extractor {
hasOutputSeekMap
=
true
;
}
else
if
(
numSamplesWithSameSize
>=
NUM_SAME_SIZE_CONSTANT_BIT_RATE_THRESHOLD
||
sampleReadResult
==
RESULT_END_OF_INPUT
)
{
seekMap
=
getConstantBitrateSeekMap
(
inputLength
);
seekMap
=
getConstantBitrateSeekMap
(
inputLength
,
(
flags
&
FLAG_ENABLE_CONSTANT_BITRATE_SEEKING_ALWAYS
)
!=
0
);
extractorOutput
.
seekMap
(
seekMap
);
hasOutputSeekMap
=
true
;
}
}
private
SeekMap
getConstantBitrateSeekMap
(
long
inputLength
)
{
private
SeekMap
getConstantBitrateSeekMap
(
long
inputLength
,
boolean
allowSeeksIfLengthUnknown
)
{
int
bitrate
=
getBitrateFromFrameSize
(
firstSampleSize
,
SAMPLE_TIME_PER_FRAME_US
);
return
new
ConstantBitrateSeekMap
(
inputLength
,
firstSamplePosition
,
bitrate
,
firstSampleSize
);
return
new
ConstantBitrateSeekMap
(
inputLength
,
firstSamplePosition
,
bitrate
,
firstSampleSize
,
allowSeeksIfLengthUnknown
);
}
@EnsuresNonNull
({
"extractorOutput"
,
"trackOutput"
})
...
...
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp3/ConstantBitrateSeeker.java
View file @
f8d60e2b
...
...
@@ -28,13 +28,23 @@ import com.google.android.exoplayer2.extractor.ConstantBitrateSeekMap;
* @param inputLength The length of the stream in bytes, or {@link C#LENGTH_UNSET} if unknown.
* @param firstFramePosition The position of the first frame in the stream.
* @param mpegAudioHeader The MPEG audio header associated with the first frame.
* @param allowSeeksIfLengthUnknown Whether to allow seeking even if the length of the content is
* unknown.
*/
public
ConstantBitrateSeeker
(
long
inputLength
,
long
firstFramePosition
,
MpegAudioUtil
.
Header
mpegAudioHeader
)
{
long
inputLength
,
long
firstFramePosition
,
MpegAudioUtil
.
Header
mpegAudioHeader
,
boolean
allowSeeksIfLengthUnknown
)
{
// Set the seeker frame size to the size of the first frame (even though some constant bitrate
// streams have variable frame sizes) to avoid the need to re-synchronize for constant frame
// size streams.
super
(
inputLength
,
firstFramePosition
,
mpegAudioHeader
.
bitrate
,
mpegAudioHeader
.
frameSize
);
super
(
inputLength
,
firstFramePosition
,
mpegAudioHeader
.
bitrate
,
mpegAudioHeader
.
frameSize
,
allowSeeksIfLengthUnknown
);
}
@Override
...
...
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp3/Mp3Extractor.java
View file @
f8d60e2b
...
...
@@ -20,6 +20,8 @@ import androidx.annotation.Nullable;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.ParserException
;
import
com.google.android.exoplayer2.PlaybackException
;
import
com.google.android.exoplayer2.Player
;
import
com.google.android.exoplayer2.audio.MpegAudioUtil
;
import
com.google.android.exoplayer2.extractor.DummyTrackOutput
;
import
com.google.android.exoplayer2.extractor.Extractor
;
...
...
@@ -56,8 +58,8 @@ public final class Mp3Extractor implements Extractor {
/**
* Flags controlling the behavior of the extractor. Possible flag values are {@link
* #FLAG_ENABLE_CONSTANT_BITRATE_SEEKING}, {@link #FLAG_ENABLE_
INDEX_SEEKING} and {@link
* #FLAG_DISABLE_ID3_METADATA}.
* #FLAG_ENABLE_CONSTANT_BITRATE_SEEKING}, {@link #FLAG_ENABLE_
CONSTANT_BITRATE_SEEKING_ALWAYS},
*
{@link #FLAG_ENABLE_INDEX_SEEKING} and {@link
#FLAG_DISABLE_ID3_METADATA}.
*/
@Documented
@Retention
(
RetentionPolicy
.
SOURCE
)
...
...
@@ -65,6 +67,7 @@ public final class Mp3Extractor implements Extractor {
flag
=
true
,
value
=
{
FLAG_ENABLE_CONSTANT_BITRATE_SEEKING
,
FLAG_ENABLE_CONSTANT_BITRATE_SEEKING_ALWAYS
,
FLAG_ENABLE_INDEX_SEEKING
,
FLAG_DISABLE_ID3_METADATA
})
...
...
@@ -77,6 +80,21 @@ public final class Mp3Extractor implements Extractor {
*/
public
static
final
int
FLAG_ENABLE_CONSTANT_BITRATE_SEEKING
=
1
;
/**
* Like {@link #FLAG_ENABLE_CONSTANT_BITRATE_SEEKING}, except that seeking is also enabled in
* cases where the content length (and hence the duration of the media) is unknown. Application
* code should ensure that requested seek positions are valid when using this flag, or be ready to
* handle playback failures reported through {@link Player.Listener#onPlayerError} with {@link
* PlaybackException#errorCode} set to {@link
* PlaybackException#ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE}.
*
* <p>If this flag is set, then the behavior enabled by {@link
* #FLAG_ENABLE_CONSTANT_BITRATE_SEEKING} is implicitly enabled.
*
* <p>This flag is ignored if {@link #FLAG_ENABLE_INDEX_SEEKING} is set.
*/
public
static
final
int
FLAG_ENABLE_CONSTANT_BITRATE_SEEKING_ALWAYS
=
1
<<
1
;
/**
* Flag to force index seeking, in which a time-to-byte mapping is built as the file is read.
*
* <p>This seeker may require to scan a significant portion of the file to compute a seek point.
...
...
@@ -88,12 +106,12 @@ public final class Mp3Extractor implements Extractor {
* provide precise enough seeking metadata.
* </ul>
*/
public
static
final
int
FLAG_ENABLE_INDEX_SEEKING
=
1
<<
1
;
public
static
final
int
FLAG_ENABLE_INDEX_SEEKING
=
1
<<
2
;
/**
* Flag to disable parsing of ID3 metadata. Can be set to save memory if ID3 metadata is not
* required.
*/
public
static
final
int
FLAG_DISABLE_ID3_METADATA
=
1
<<
2
;
public
static
final
int
FLAG_DISABLE_ID3_METADATA
=
1
<<
3
;
/** Predicate that matches ID3 frames containing only required gapless/seeking metadata. */
private
static
final
FramePredicate
REQUIRED_ID3_FRAME_PREDICATE
=
...
...
@@ -158,6 +176,9 @@ public final class Mp3Extractor implements Extractor {
* C#TIME_UNSET} if forcing is not required.
*/
public
Mp3Extractor
(
@Flags
int
flags
,
long
forcedFirstSampleTimestampUs
)
{
if
((
flags
&
FLAG_ENABLE_CONSTANT_BITRATE_SEEKING_ALWAYS
)
!=
0
)
{
flags
|=
FLAG_ENABLE_CONSTANT_BITRATE_SEEKING
;
}
this
.
flags
=
flags
;
this
.
forcedFirstSampleTimestampUs
=
forcedFirstSampleTimestampUs
;
scratch
=
new
ParsableByteArray
(
SCRATCH_LENGTH
);
...
...
@@ -446,7 +467,9 @@ public final class Mp3Extractor implements Extractor {
if
(
resultSeeker
==
null
||
(!
resultSeeker
.
isSeekable
()
&&
(
flags
&
FLAG_ENABLE_CONSTANT_BITRATE_SEEKING
)
!=
0
))
{
resultSeeker
=
getConstantBitrateSeeker
(
input
);
resultSeeker
=
getConstantBitrateSeeker
(
input
,
(
flags
&
FLAG_ENABLE_CONSTANT_BITRATE_SEEKING_ALWAYS
)
!=
0
);
}
return
resultSeeker
;
...
...
@@ -485,7 +508,7 @@ public final class Mp3Extractor implements Extractor {
input
.
skipFully
(
synchronizedHeader
.
frameSize
);
if
(
seeker
!=
null
&&
!
seeker
.
isSeekable
()
&&
seekHeader
==
SEEK_HEADER_INFO
)
{
// Fall back to constant bitrate seeking for Info headers missing a table of contents.
return
getConstantBitrateSeeker
(
input
);
return
getConstantBitrateSeeker
(
input
,
/* allowSeeksIfLengthUnknown= */
false
);
}
}
else
if
(
seekHeader
==
SEEK_HEADER_VBRI
)
{
seeker
=
VbriSeeker
.
create
(
input
.
getLength
(),
input
.
getPosition
(),
synchronizedHeader
,
frame
);
...
...
@@ -499,11 +522,13 @@ public final class Mp3Extractor implements Extractor {
}
/** Peeks the next frame and returns a {@link ConstantBitrateSeeker} based on its bitrate. */
private
Seeker
getConstantBitrateSeeker
(
ExtractorInput
input
)
throws
IOException
{
private
Seeker
getConstantBitrateSeeker
(
ExtractorInput
input
,
boolean
allowSeeksIfLengthUnknown
)
throws
IOException
{
input
.
peekFully
(
scratch
.
getData
(),
0
,
4
);
scratch
.
setPosition
(
0
);
synchronizedHeader
.
setForHeaderData
(
scratch
.
readInt
());
return
new
ConstantBitrateSeeker
(
input
.
getLength
(),
input
.
getPosition
(),
synchronizedHeader
);
return
new
ConstantBitrateSeeker
(
input
.
getLength
(),
input
.
getPosition
(),
synchronizedHeader
,
allowSeeksIfLengthUnknown
);
}
@EnsuresNonNull
({
"extractorOutput"
,
"realTrackOutput"
})
...
...
library/extractor/src/main/java/com/google/android/exoplayer2/extractor/ts/AdtsExtractor.java
View file @
f8d60e2b
...
...
@@ -22,6 +22,8 @@ import static com.google.android.exoplayer2.metadata.id3.Id3Decoder.ID3_TAG;
import
androidx.annotation.IntDef
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.ParserException
;
import
com.google.android.exoplayer2.PlaybackException
;
import
com.google.android.exoplayer2.Player
;
import
com.google.android.exoplayer2.extractor.ConstantBitrateSeekMap
;
import
com.google.android.exoplayer2.extractor.Extractor
;
import
com.google.android.exoplayer2.extractor.ExtractorInput
;
...
...
@@ -48,14 +50,15 @@ public final class AdtsExtractor implements Extractor {
public
static
final
ExtractorsFactory
FACTORY
=
()
->
new
Extractor
[]
{
new
AdtsExtractor
()};
/**
* Flags controlling the behavior of the extractor. Possible flag value is {@link
* #FLAG_ENABLE_CONSTANT_BITRATE_SEEKING}.
* Flags controlling the behavior of the extractor. Possible flag values are {@link
* #FLAG_ENABLE_CONSTANT_BITRATE_SEEKING} and {@link
* #FLAG_ENABLE_CONSTANT_BITRATE_SEEKING_ALWAYS}.
*/
@Documented
@Retention
(
RetentionPolicy
.
SOURCE
)
@IntDef
(
flag
=
true
,
value
=
{
FLAG_ENABLE_CONSTANT_BITRATE_SEEKING
})
value
=
{
FLAG_ENABLE_CONSTANT_BITRATE_SEEKING
,
FLAG_ENABLE_CONSTANT_BITRATE_SEEKING_ALWAYS
})
public
@interface
Flags
{}
/**
* Flag to force enable seeking using a constant bitrate assumption in cases where seeking would
...
...
@@ -65,6 +68,18 @@ public final class AdtsExtractor implements Extractor {
* are not precise, especially when the stream bitrate varies a lot.
*/
public
static
final
int
FLAG_ENABLE_CONSTANT_BITRATE_SEEKING
=
1
;
/**
* Like {@link #FLAG_ENABLE_CONSTANT_BITRATE_SEEKING}, except that seeking is also enabled in
* cases where the content length (and hence the duration of the media) is unknown. Application
* code should ensure that requested seek positions are valid when using this flag, or be ready to
* handle playback failures reported through {@link Player.Listener#onPlayerError} with {@link
* PlaybackException#errorCode} set to {@link
* PlaybackException#ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE}.
*
* <p>If this flag is set, then the behavior enabled by {@link
* #FLAG_ENABLE_CONSTANT_BITRATE_SEEKING} is implicitly enabled as well.
*/
public
static
final
int
FLAG_ENABLE_CONSTANT_BITRATE_SEEKING_ALWAYS
=
1
<<
1
;
private
static
final
int
MAX_PACKET_SIZE
=
2
*
1024
;
/**
...
...
@@ -105,6 +120,9 @@ public final class AdtsExtractor implements Extractor {
* @param flags Flags that control the extractor's behavior.
*/
public
AdtsExtractor
(
@Flags
int
flags
)
{
if
((
flags
&
FLAG_ENABLE_CONSTANT_BITRATE_SEEKING_ALWAYS
)
!=
0
)
{
flags
|=
FLAG_ENABLE_CONSTANT_BITRATE_SEEKING
;
}
this
.
flags
=
flags
;
reader
=
new
AdtsReader
(
true
);
packetBuffer
=
new
ParsableByteArray
(
MAX_PACKET_SIZE
);
...
...
@@ -182,14 +200,16 @@ public final class AdtsExtractor implements Extractor {
long
inputLength
=
input
.
getLength
();
boolean
canUseConstantBitrateSeeking
=
(
flags
&
FLAG_ENABLE_CONSTANT_BITRATE_SEEKING
)
!=
0
&&
inputLength
!=
C
.
LENGTH_UNSET
;
(
flags
&
FLAG_ENABLE_CONSTANT_BITRATE_SEEKING_ALWAYS
)
!=
0
||
((
flags
&
FLAG_ENABLE_CONSTANT_BITRATE_SEEKING
)
!=
0
&&
inputLength
!=
C
.
LENGTH_UNSET
);
if
(
canUseConstantBitrateSeeking
)
{
calculateAverageFrameSize
(
input
);
}
int
bytesRead
=
input
.
read
(
packetBuffer
.
getData
(),
0
,
MAX_PACKET_SIZE
);
boolean
readEndOfStream
=
bytesRead
==
RESULT_END_OF_INPUT
;
maybeOutputSeekMap
(
inputLength
,
canUseConstantBitrateSeeking
,
readEndOfStream
);
maybeOutputSeekMap
(
inputLength
,
readEndOfStream
);
if
(
readEndOfStream
)
{
return
RESULT_END_OF_INPUT
;
}
...
...
@@ -231,12 +251,13 @@ public final class AdtsExtractor implements Extractor {
}
@RequiresNonNull
(
"extractorOutput"
)
private
void
maybeOutputSeekMap
(
long
inputLength
,
boolean
canUseConstantBitrateSeeking
,
boolean
readEndOfStream
)
{
private
void
maybeOutputSeekMap
(
long
inputLength
,
boolean
readEndOfStream
)
{
if
(
hasOutputSeekMap
)
{
return
;
}
boolean
useConstantBitrateSeeking
=
canUseConstantBitrateSeeking
&&
averageFrameSize
>
0
;
boolean
useConstantBitrateSeeking
=
(
flags
&
FLAG_ENABLE_CONSTANT_BITRATE_SEEKING
)
!=
0
&&
averageFrameSize
>
0
;
if
(
useConstantBitrateSeeking
&&
reader
.
getSampleDurationUs
()
==
C
.
TIME_UNSET
&&
!
readEndOfStream
)
{
...
...
@@ -246,7 +267,9 @@ public final class AdtsExtractor implements Extractor {
}
if
(
useConstantBitrateSeeking
&&
reader
.
getSampleDurationUs
()
!=
C
.
TIME_UNSET
)
{
extractorOutput
.
seekMap
(
getConstantBitrateSeekMap
(
inputLength
));
extractorOutput
.
seekMap
(
getConstantBitrateSeekMap
(
inputLength
,
(
flags
&
FLAG_ENABLE_CONSTANT_BITRATE_SEEKING_ALWAYS
)
!=
0
));
}
else
{
extractorOutput
.
seekMap
(
new
SeekMap
.
Unseekable
(
C
.
TIME_UNSET
));
}
...
...
@@ -314,9 +337,10 @@ public final class AdtsExtractor implements Extractor {
hasCalculatedAverageFrameSize
=
true
;
}
private
SeekMap
getConstantBitrateSeekMap
(
long
inputLength
)
{
private
SeekMap
getConstantBitrateSeekMap
(
long
inputLength
,
boolean
allowSeeksIfLengthUnknown
)
{
int
bitrate
=
getBitrateFromFrameSize
(
averageFrameSize
,
reader
.
getSampleDurationUs
());
return
new
ConstantBitrateSeekMap
(
inputLength
,
firstFramePosition
,
bitrate
,
averageFrameSize
);
return
new
ConstantBitrateSeekMap
(
inputLength
,
firstFramePosition
,
bitrate
,
averageFrameSize
,
allowSeeksIfLengthUnknown
);
}
/**
...
...
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