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
e2ff401e
authored
Oct 17, 2016
by
ojw28
Committed by
GitHub
Oct 17, 2016
Browse files
Options
_('Browse Files')
Download
Plain Diff
Merge pull request #1958 from google/dev-v2
Merge dev-v2 into dev-v2-id3
parents
66652f65
cecb1f5f
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
37 changed files
with
458 additions
and
287 deletions
RELEASENOTES.md
build.gradle
demo/src/main/AndroidManifest.xml
extensions/cronet/src/androidTest/java/com/google/android/exoplayer2/ext/cronet/CronetDataSourceTest.java
extensions/cronet/src/main/java/com/google/android/exoplayer2/ext/cronet/CronetDataSource.java
library/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/AdtsReaderTest.java
library/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/TsExtractorTest.java
library/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java
library/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java
library/src/main/java/com/google/android/exoplayer2/drm/HttpMediaDrmCallback.java
library/src/main/java/com/google/android/exoplayer2/extractor/Extractor.java
library/src/main/java/com/google/android/exoplayer2/extractor/ExtractorOutput.java
library/src/main/java/com/google/android/exoplayer2/extractor/ts/Ac3Extractor.java
library/src/main/java/com/google/android/exoplayer2/extractor/ts/Ac3Reader.java
library/src/main/java/com/google/android/exoplayer2/extractor/ts/AdtsExtractor.java
library/src/main/java/com/google/android/exoplayer2/extractor/ts/AdtsReader.java
library/src/main/java/com/google/android/exoplayer2/extractor/ts/DefaultStreamReaderFactory.java
library/src/main/java/com/google/android/exoplayer2/extractor/ts/DtsReader.java
library/src/main/java/com/google/android/exoplayer2/extractor/ts/ElementaryStreamReader.java
library/src/main/java/com/google/android/exoplayer2/extractor/ts/H262Reader.java
library/src/main/java/com/google/android/exoplayer2/extractor/ts/H264Reader.java
library/src/main/java/com/google/android/exoplayer2/extractor/ts/H265Reader.java
library/src/main/java/com/google/android/exoplayer2/extractor/ts/Id3Reader.java
library/src/main/java/com/google/android/exoplayer2/extractor/ts/MpegAudioReader.java
library/src/main/java/com/google/android/exoplayer2/extractor/ts/PsExtractor.java
library/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java
library/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java
library/src/main/java/com/google/android/exoplayer2/source/chunk/ChunkExtractorWrapper.java
library/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParser.java
library/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java
library/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java
library/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java
library/src/main/java/com/google/android/exoplayer2/upstream/DefaultHttpDataSource.java
library/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java
playbacktests/src/main/AndroidManifest.xml
testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeExtractorOutput.java
testutils/src/main/java/com/google/android/exoplayer2/testutil/TestUtil.java
RELEASENOTES.md
View file @
e2ff401e
# Release notes #
### r2.0.3 ###
This release contains important bug fixes. Users of r2.0.0, r2.0.1 and r2.0.2
should proactively update to this version.
*
Fixed NullPointerException in ExtractorMediaSource
(
[
#1914
](
https://github.com/google/ExoPlayer/issues/1914
)
.
*
Fixed NullPointerException in HlsMediaPeriod
(
[
#1907
](
https://github.com/google/ExoPlayer/issues/1907
)
.
*
Fixed memory leak in PlaybackControlView
(
[
#1908
](
https://github.com/google/ExoPlayer/issues/1908
)
.
*
Fixed strict mode violation when using
SimpleExoPlayer.setVideoPlayerTextureView().
*
Fixed L3 Widevine provisioning
(
[
#1925
](
https://github.com/google/ExoPlayer/issues/1925
)
.
*
Fixed hiding of controls with use_controller="false"
(
[
#1919
](
https://github.com/google/ExoPlayer/issues/1919
)
.
*
Improvements to Cronet network stack extension.
*
Misc bug fixes.
### r2.0.2 ###
*
Fixes for MergingMediaSource and sideloaded subtitles.
...
...
@@ -88,6 +108,13 @@ some of the motivations behind ExoPlayer 2.x
*
Suppressed "Sending message to a Handler on a dead thread" warnings
(
[
#426
](
https://github.com/google/ExoPlayer/issues/426
)
).
### r1.5.12 ###
*
Improvements to Cronet network stack extension.
*
Fix bug in demo app introduced in r1.5.11 that caused L3 Widevine
provisioning requests to fail.
*
Misc bugfixes.
### r1.5.11 ###
*
Cronet network stack extension.
...
...
build.gradle
View file @
e2ff401e
...
...
@@ -35,7 +35,7 @@ allprojects {
releaseRepoName
=
'exoplayer'
releaseUserOrg
=
'google'
releaseGroupId
=
'com.google.android.exoplayer'
releaseVersion
=
'r2.0.
2
'
releaseVersion
=
'r2.0.
3
'
releaseWebsite
=
'https://github.com/google/ExoPlayer'
}
}
demo/src/main/AndroidManifest.xml
View file @
e2ff401e
...
...
@@ -16,8 +16,8 @@
<manifest
xmlns:android=
"http://schemas.android.com/apk/res/android"
package=
"com.google.android.exoplayer2.demo"
android:versionCode=
"200
2
"
android:versionName=
"2.0.
2
"
>
android:versionCode=
"200
3
"
android:versionName=
"2.0.
3
"
>
<uses-permission
android:name=
"android.permission.INTERNET"
/>
<uses-permission
android:name=
"android.permission.READ_EXTERNAL_STORAGE"
/>
...
...
extensions/cronet/src/androidTest/java/com/google/android/exoplayer2/ext/cronet/CronetDataSourceTest.java
View file @
e2ff401e
This diff is collapsed.
Click to expand it.
extensions/cronet/src/main/java/com/google/android/exoplayer2/ext/cronet/CronetDataSource.java
View file @
e2ff401e
...
...
@@ -103,6 +103,7 @@ public class CronetDataSource extends UrlRequest.Callback implements HttpDataSou
// Accessed by the calling thread only.
private
boolean
opened
;
private
long
bytesToSkip
;
private
long
bytesRemaining
;
// Written from the calling thread only. currentUrlRequest.start() calls ensure writes are visible
...
...
@@ -242,9 +243,10 @@ public class CronetDataSource extends UrlRequest.Callback implements HttpDataSou
}
}
// TODO: Handle the case where we requested a range starting from a non-zero position and
// received a 200 rather than a 206. This occurs if the server does not support partial
// requests, and requires that the source skips to the requested position.
// If we requested a range starting from a non-zero position and received a 200 rather than a
// 206, then the server does not support partial requests. We'll need to manually skip to the
// requested position.
bytesToSkip
=
responseCode
==
200
&&
dataSpec
.
position
!=
0
?
dataSpec
.
position
:
0
;
// Calculate the content length.
if
(!
getIsCompressed
(
responseInfo
))
{
...
...
@@ -281,7 +283,7 @@ public class CronetDataSource extends UrlRequest.Callback implements HttpDataSou
readBuffer
=
ByteBuffer
.
allocateDirect
(
READ_BUFFER_SIZE_BYTES
);
readBuffer
.
limit
(
0
);
}
if
(!
readBuffer
.
hasRemaining
())
{
while
(!
readBuffer
.
hasRemaining
())
{
// Fill readBuffer with more data from Cronet.
operation
.
close
();
readBuffer
.
clear
();
...
...
@@ -301,6 +303,12 @@ public class CronetDataSource extends UrlRequest.Callback implements HttpDataSou
}
else
{
// The operation didn't time out, fail or finish, and therefore data must have been read.
readBuffer
.
flip
();
Assertions
.
checkState
(
readBuffer
.
hasRemaining
());
if
(
bytesToSkip
>
0
)
{
int
bytesSkipped
=
(
int
)
Math
.
min
(
readBuffer
.
remaining
(),
bytesToSkip
);
readBuffer
.
position
(
readBuffer
.
position
()
+
bytesSkipped
);
bytesToSkip
-=
bytesSkipped
;
}
}
}
...
...
@@ -408,8 +416,10 @@ public class CronetDataSource extends UrlRequest.Callback implements HttpDataSou
executor
,
cronetEngine
);
// Set the headers.
synchronized
(
requestProperties
)
{
if
(
dataSpec
.
postBody
!=
null
&&
!
requestProperties
.
containsKey
(
CONTENT_TYPE
))
{
throw
new
OpenException
(
"POST request must set Content-Type"
,
dataSpec
,
Status
.
IDLE
);
if
(
dataSpec
.
postBody
!=
null
&&
dataSpec
.
postBody
.
length
!=
0
&&
!
requestProperties
.
containsKey
(
CONTENT_TYPE
))
{
throw
new
OpenException
(
"POST request with non-empty body must set Content-Type"
,
dataSpec
,
Status
.
IDLE
);
}
for
(
Entry
<
String
,
String
>
headerEntry
:
requestProperties
.
entrySet
())
{
requestBuilder
.
addHeader
(
headerEntry
.
getKey
(),
headerEntry
.
getValue
());
...
...
@@ -426,10 +436,13 @@ public class CronetDataSource extends UrlRequest.Callback implements HttpDataSou
}
requestBuilder
.
addHeader
(
"Range"
,
rangeValue
.
toString
());
}
// Set the body.
// Set the
method and (if non-empty) the
body.
if
(
dataSpec
.
postBody
!=
null
)
{
requestBuilder
.
setUploadDataProvider
(
new
ByteArrayUploadDataProvider
(
dataSpec
.
postBody
),
executor
);
requestBuilder
.
setHttpMethod
(
"POST"
);
if
(
dataSpec
.
postBody
.
length
!=
0
)
{
requestBuilder
.
setUploadDataProvider
(
new
ByteArrayUploadDataProvider
(
dataSpec
.
postBody
),
executor
);
}
}
return
requestBuilder
.
build
();
}
...
...
library/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/AdtsReaderTest.java
View file @
e2ff401e
...
...
@@ -16,6 +16,8 @@
package
com
.
google
.
android
.
exoplayer2
.
extractor
.
ts
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.extractor.ts.ElementaryStreamReader.TrackIdGenerator
;
import
com.google.android.exoplayer2.testutil.FakeExtractorOutput
;
import
com.google.android.exoplayer2.testutil.FakeTrackOutput
;
import
com.google.android.exoplayer2.testutil.TestUtil
;
import
com.google.android.exoplayer2.util.ParsableByteArray
;
...
...
@@ -66,9 +68,12 @@ public class AdtsReaderTest extends TestCase {
@Override
protected
void
setUp
()
throws
Exception
{
adtsOutput
=
new
FakeTrackOutput
();
id3Output
=
new
FakeTrackOutput
();
adtsReader
=
new
AdtsReader
(
adtsOutput
,
id3Output
);
FakeExtractorOutput
fakeExtractorOutput
=
new
FakeExtractorOutput
();
adtsOutput
=
fakeExtractorOutput
.
track
(
0
);
id3Output
=
fakeExtractorOutput
.
track
(
1
);
adtsReader
=
new
AdtsReader
(
true
);
TrackIdGenerator
idGenerator
=
new
TrackIdGenerator
(
0
,
1
);
adtsReader
.
init
(
fakeExtractorOutput
,
idGenerator
);
data
=
new
ParsableByteArray
(
TEST_DATA
);
firstFeed
=
true
;
}
...
...
library/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/TsExtractorTest.java
View file @
e2ff401e
...
...
@@ -22,6 +22,7 @@ import com.google.android.exoplayer2.extractor.ExtractorOutput;
import
com.google.android.exoplayer2.extractor.PositionHolder
;
import
com.google.android.exoplayer2.extractor.TimestampAdjuster
;
import
com.google.android.exoplayer2.extractor.TrackOutput
;
import
com.google.android.exoplayer2.extractor.ts.ElementaryStreamReader.EsInfo
;
import
com.google.android.exoplayer2.testutil.FakeExtractorInput
;
import
com.google.android.exoplayer2.testutil.FakeExtractorOutput
;
import
com.google.android.exoplayer2.testutil.FakeTrackOutput
;
...
...
@@ -72,7 +73,7 @@ public final class TsExtractorTest extends InstrumentationTestCase {
public
void
testCustomPesReader
()
throws
Exception
{
CustomEsReaderFactory
factory
=
new
CustomEsReaderFactory
();
TsExtractor
tsExtractor
=
new
TsExtractor
(
new
TimestampAdjuster
(
0
),
factory
);
TsExtractor
tsExtractor
=
new
TsExtractor
(
new
TimestampAdjuster
(
0
),
factory
,
false
);
FakeExtractorInput
input
=
new
FakeExtractorInput
.
Builder
()
.
setData
(
TestUtil
.
getByteArray
(
getInstrumentation
(),
"ts/sample.ts"
))
.
setSimulateIOErrors
(
false
)
...
...
@@ -107,12 +108,12 @@ public final class TsExtractorTest extends InstrumentationTestCase {
private
static
final
class
CustomEsReader
extends
ElementaryStreamReader
{
private
final
String
language
;
private
TrackOutput
output
;
public
int
packetsRead
=
0
;
public
CustomEsReader
(
TrackOutput
output
,
String
language
)
{
super
(
output
);
output
.
format
(
Format
.
createTextSampleFormat
(
"Overriding format"
,
"mime"
,
null
,
0
,
0
,
language
,
null
,
0
));
public
CustomEsReader
(
String
language
)
{
this
.
language
=
language
;
}
@Override
...
...
@@ -120,6 +121,13 @@ public final class TsExtractorTest extends InstrumentationTestCase {
}
@Override
public
void
init
(
ExtractorOutput
extractorOutput
,
TrackIdGenerator
idGenerator
)
{
output
=
extractorOutput
.
track
(
idGenerator
.
getNextId
());
output
.
format
(
Format
.
createTextSampleFormat
(
"Overriding format"
,
"mime"
,
null
,
0
,
0
,
language
,
null
,
0
));
}
@Override
public
void
packetStarted
(
long
pesTimeUs
,
boolean
dataAlignmentIndicator
)
{
}
...
...
@@ -148,16 +156,12 @@ public final class TsExtractorTest extends InstrumentationTestCase {
}
@Override
public
ElementaryStreamReader
onPmtEntry
(
int
pid
,
int
streamType
,
ElementaryStreamReader
.
EsInfo
esInfo
,
ExtractorOutput
output
)
{
public
ElementaryStreamReader
createStreamReader
(
int
streamType
,
EsInfo
esInfo
)
{
if
(
streamType
==
3
)
{
// We need to manually avoid a duplicate custom reader creation.
if
(
reader
==
null
)
{
reader
=
new
CustomEsReader
(
output
.
track
(
pid
),
esInfo
.
language
);
}
reader
=
new
CustomEsReader
(
esInfo
.
language
);
return
reader
;
}
else
{
return
defaultFactory
.
onPmtEntry
(
pid
,
streamType
,
esInfo
,
output
);
return
defaultFactory
.
createStreamReader
(
streamType
,
esInfo
);
}
}
...
...
library/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java
View file @
e2ff401e
...
...
@@ -23,7 +23,7 @@ public interface ExoPlayerLibraryInfo {
/**
* The version of the library, expressed as a string.
*/
String
VERSION
=
"2.0.
2
"
;
String
VERSION
=
"2.0.
3
"
;
/**
* The version of the library, expressed as an integer.
...
...
@@ -32,7 +32,7 @@ public interface ExoPlayerLibraryInfo {
* corresponding integer version 1002003 (001-002-003), and "123.45.6" has the corresponding
* integer version 123045006 (123-045-006).
*/
int
VERSION_INT
=
200000
2
;
int
VERSION_INT
=
200000
3
;
/**
* Whether the library was compiled with {@link com.google.android.exoplayer2.util.Assertions}
...
...
library/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java
View file @
e2ff401e
...
...
@@ -108,6 +108,7 @@ public final class SimpleExoPlayer implements ExoPlayer {
private
Format
audioFormat
;
private
Surface
surface
;
private
boolean
ownsSurface
;
private
SurfaceHolder
surfaceHolder
;
private
TextureView
textureView
;
private
TextRenderer
.
Output
textOutput
;
...
...
@@ -206,7 +207,7 @@ public final class SimpleExoPlayer implements ExoPlayer {
*/
public
void
setVideoSurface
(
Surface
surface
)
{
removeSurfaceCallbacks
();
setVideoSurfaceInternal
(
surface
);
setVideoSurfaceInternal
(
surface
,
false
);
}
/**
...
...
@@ -219,9 +220,9 @@ public final class SimpleExoPlayer implements ExoPlayer {
removeSurfaceCallbacks
();
this
.
surfaceHolder
=
surfaceHolder
;
if
(
surfaceHolder
==
null
)
{
setVideoSurfaceInternal
(
null
);
setVideoSurfaceInternal
(
null
,
false
);
}
else
{
setVideoSurfaceInternal
(
surfaceHolder
.
getSurface
());
setVideoSurfaceInternal
(
surfaceHolder
.
getSurface
()
,
false
);
surfaceHolder
.
addCallback
(
componentListener
);
}
}
...
...
@@ -246,13 +247,13 @@ public final class SimpleExoPlayer implements ExoPlayer {
removeSurfaceCallbacks
();
this
.
textureView
=
textureView
;
if
(
textureView
==
null
)
{
setVideoSurfaceInternal
(
null
);
setVideoSurfaceInternal
(
null
,
true
);
}
else
{
if
(
textureView
.
getSurfaceTextureListener
()
!=
null
)
{
Log
.
w
(
TAG
,
"Replacing existing SurfaceTextureListener."
);
}
SurfaceTexture
surfaceTexture
=
textureView
.
getSurfaceTexture
();
setVideoSurfaceInternal
(
surfaceTexture
==
null
?
null
:
new
Surface
(
surfaceTexture
));
setVideoSurfaceInternal
(
surfaceTexture
==
null
?
null
:
new
Surface
(
surfaceTexture
)
,
true
);
textureView
.
setSurfaceTextureListener
(
componentListener
);
}
}
...
...
@@ -477,6 +478,12 @@ public final class SimpleExoPlayer implements ExoPlayer {
public
void
release
()
{
player
.
release
();
removeSurfaceCallbacks
();
if
(
surface
!=
null
)
{
if
(
ownsSurface
)
{
surface
.
release
();
}
surface
=
null
;
}
}
@Override
...
...
@@ -627,8 +634,9 @@ public final class SimpleExoPlayer implements ExoPlayer {
}
}
private
void
setVideoSurfaceInternal
(
Surface
surface
)
{
this
.
surface
=
surface
;
private
void
setVideoSurfaceInternal
(
Surface
surface
,
boolean
ownsSurface
)
{
// Note: We don't turn this method into a no-op if the surface is being replaced with itself
// so as to ensure onRenderedFirstFrame callbacks are still called in this case.
ExoPlayerMessage
[]
messages
=
new
ExoPlayerMessage
[
videoRendererCount
];
int
count
=
0
;
for
(
Renderer
renderer
:
renderers
)
{
...
...
@@ -636,12 +644,18 @@ public final class SimpleExoPlayer implements ExoPlayer {
messages
[
count
++]
=
new
ExoPlayerMessage
(
renderer
,
C
.
MSG_SET_SURFACE
,
surface
);
}
}
if
(
surface
==
null
)
{
// Block to ensure that the surface is not accessed after the method returns.
if
(
this
.
surface
!=
null
&&
this
.
surface
!=
surface
)
{
// If we created this surface, we are responsible for releasing it.
if
(
this
.
ownsSurface
)
{
this
.
surface
.
release
();
}
// We're replacing a surface. Block to ensure that it's not accessed after the method returns.
player
.
blockingSendMessages
(
messages
);
}
else
{
player
.
sendMessages
(
messages
);
}
this
.
surface
=
surface
;
this
.
ownsSurface
=
ownsSurface
;
}
private
final
class
ComponentListener
implements
VideoRendererEventListener
,
...
...
@@ -790,7 +804,7 @@ public final class SimpleExoPlayer implements ExoPlayer {
@Override
public
void
surfaceCreated
(
SurfaceHolder
holder
)
{
setVideoSurfaceInternal
(
holder
.
getSurface
());
setVideoSurfaceInternal
(
holder
.
getSurface
()
,
false
);
}
@Override
...
...
@@ -800,14 +814,14 @@ public final class SimpleExoPlayer implements ExoPlayer {
@Override
public
void
surfaceDestroyed
(
SurfaceHolder
holder
)
{
setVideoSurfaceInternal
(
null
);
setVideoSurfaceInternal
(
null
,
false
);
}
// TextureView.SurfaceTextureListener implementation
@Override
public
void
onSurfaceTextureAvailable
(
SurfaceTexture
surfaceTexture
,
int
width
,
int
height
)
{
setVideoSurfaceInternal
(
new
Surface
(
surfaceTexture
));
setVideoSurfaceInternal
(
new
Surface
(
surfaceTexture
)
,
true
);
}
@Override
...
...
@@ -817,7 +831,7 @@ public final class SimpleExoPlayer implements ExoPlayer {
@Override
public
boolean
onSurfaceTextureDestroyed
(
SurfaceTexture
surfaceTexture
)
{
setVideoSurface
(
null
);
setVideoSurface
Internal
(
null
,
true
);
return
true
;
}
...
...
library/src/main/java/com/google/android/exoplayer2/drm/HttpMediaDrmCallback.java
View file @
e2ff401e
...
...
@@ -71,7 +71,7 @@ public final class HttpMediaDrmCallback implements MediaDrmCallback {
@Override
public
byte
[]
executeProvisionRequest
(
UUID
uuid
,
ProvisionRequest
request
)
throws
IOException
{
String
url
=
request
.
getDefaultUrl
()
+
"&signedRequest="
+
new
String
(
request
.
getData
());
return
executePost
(
url
,
n
ull
,
null
);
return
executePost
(
url
,
n
ew
byte
[
0
]
,
null
);
}
@Override
...
...
@@ -81,6 +81,7 @@ public final class HttpMediaDrmCallback implements MediaDrmCallback {
url
=
defaultUrl
;
}
Map
<
String
,
String
>
requestProperties
=
new
HashMap
<>();
requestProperties
.
put
(
"Content-Type"
,
"application/octet-stream"
);
if
(
C
.
PLAYREADY_UUID
.
equals
(
uuid
))
{
requestProperties
.
putAll
(
PLAYREADY_KEY_REQUEST_PROPERTIES
);
}
...
...
@@ -93,8 +94,6 @@ public final class HttpMediaDrmCallback implements MediaDrmCallback {
private
byte
[]
executePost
(
String
url
,
byte
[]
data
,
Map
<
String
,
String
>
requestProperties
)
throws
IOException
{
HttpDataSource
dataSource
=
dataSourceFactory
.
createDataSource
();
// Note: This will be overridden by a Content-Type in requestProperties, if one is set.
dataSource
.
setRequestProperty
(
"Content-Type"
,
"application/octet-stream"
);
if
(
requestProperties
!=
null
)
{
for
(
Map
.
Entry
<
String
,
String
>
requestProperty
:
requestProperties
.
entrySet
())
{
dataSource
.
setRequestProperty
(
requestProperty
.
getKey
(),
requestProperty
.
getValue
());
...
...
library/src/main/java/com/google/android/exoplayer2/extractor/Extractor.java
View file @
e2ff401e
...
...
@@ -56,7 +56,7 @@ public interface Extractor {
boolean
sniff
(
ExtractorInput
input
)
throws
IOException
,
InterruptedException
;
/**
* Initializes the extractor with an {@link ExtractorOutput}.
* Initializes the extractor with an {@link ExtractorOutput}.
Called at most once.
*
* @param output An {@link ExtractorOutput} to receive extracted data.
*/
...
...
library/src/main/java/com/google/android/exoplayer2/extractor/ExtractorOutput.java
View file @
e2ff401e
...
...
@@ -21,18 +21,19 @@ package com.google.android.exoplayer2.extractor;
public
interface
ExtractorOutput
{
/**
* Called
when the {@link Extractor} identifies the existence of a track in the stream
.
* Called
by the {@link Extractor} to get the {@link TrackOutput} for a specific track
.
* <p>
* Returns a {@link TrackOutput} that will receive track level data belonging to the track.
* The same {@link TrackOutput} is returned if multiple calls are made with the same
* {@code trackId}.
*
* @param trackId A
unique
track identifier.
* @return The {@link TrackOutput}
that should receive track level data belonging to the track
.
* @param trackId A track identifier.
* @return The {@link TrackOutput}
for the given track identifier
.
*/
TrackOutput
track
(
int
trackId
);
/**
* Called when all tracks have been identified, meaning
that {@link #track(int)} will not
be
*
called again
.
* Called when all tracks have been identified, meaning
no new {@code trackId} values will
be
*
passed to {@link #track(int)}
.
*/
void
endTracks
();
...
...
library/src/main/java/com/google/android/exoplayer2/extractor/ts/Ac3Extractor.java
View file @
e2ff401e
...
...
@@ -23,6 +23,7 @@ import com.google.android.exoplayer2.extractor.ExtractorOutput;
import
com.google.android.exoplayer2.extractor.ExtractorsFactory
;
import
com.google.android.exoplayer2.extractor.PositionHolder
;
import
com.google.android.exoplayer2.extractor.SeekMap
;
import
com.google.android.exoplayer2.extractor.ts.ElementaryStreamReader.TrackIdGenerator
;
import
com.google.android.exoplayer2.util.ParsableByteArray
;
import
com.google.android.exoplayer2.util.Util
;
...
...
@@ -117,7 +118,8 @@ public final class Ac3Extractor implements Extractor {
@Override
public
void
init
(
ExtractorOutput
output
)
{
reader
=
new
Ac3Reader
(
output
.
track
(
0
));
// TODO: Add support for embedded ID3.
reader
=
new
Ac3Reader
();
// TODO: Add support for embedded ID3.
reader
.
init
(
output
,
new
TrackIdGenerator
(
0
,
1
));
output
.
endTracks
();
output
.
seekMap
(
new
SeekMap
.
Unseekable
(
C
.
TIME_UNSET
));
}
...
...
library/src/main/java/com/google/android/exoplayer2/extractor/ts/Ac3Reader.java
View file @
e2ff401e
...
...
@@ -18,6 +18,7 @@ package com.google.android.exoplayer2.extractor.ts;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.audio.Ac3Util
;
import
com.google.android.exoplayer2.extractor.ExtractorOutput
;
import
com.google.android.exoplayer2.extractor.TrackOutput
;
import
com.google.android.exoplayer2.util.ParsableBitArray
;
import
com.google.android.exoplayer2.util.ParsableByteArray
;
...
...
@@ -37,6 +38,8 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
private
final
ParsableByteArray
headerScratchBytes
;
private
final
String
language
;
private
TrackOutput
output
;
private
int
state
;
private
int
bytesRead
;
...
...
@@ -54,21 +57,17 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
/**
* Constructs a new reader for (E-)AC-3 elementary streams.
*
* @param output Track output for extracted samples.
*/
public
Ac3Reader
(
TrackOutput
output
)
{
this
(
output
,
null
);
public
Ac3Reader
()
{
this
(
null
);
}
/**
* Constructs a new reader for (E-)AC-3 elementary streams.
*
* @param output Track output for extracted samples.
* @param language Track language.
*/
public
Ac3Reader
(
TrackOutput
output
,
String
language
)
{
super
(
output
);
public
Ac3Reader
(
String
language
)
{
headerScratchBits
=
new
ParsableBitArray
(
new
byte
[
HEADER_SIZE
]);
headerScratchBytes
=
new
ParsableByteArray
(
headerScratchBits
.
data
);
state
=
STATE_FINDING_SYNC
;
...
...
@@ -83,6 +82,11 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
}
@Override
public
void
init
(
ExtractorOutput
extractorOutput
,
TrackIdGenerator
generator
)
{
output
=
extractorOutput
.
track
(
generator
.
getNextId
());
}
@Override
public
void
packetStarted
(
long
pesTimeUs
,
boolean
dataAlignmentIndicator
)
{
timeUs
=
pesTimeUs
;
}
...
...
library/src/main/java/com/google/android/exoplayer2/extractor/ts/AdtsExtractor.java
View file @
e2ff401e
...
...
@@ -22,6 +22,7 @@ import com.google.android.exoplayer2.extractor.ExtractorOutput;
import
com.google.android.exoplayer2.extractor.ExtractorsFactory
;
import
com.google.android.exoplayer2.extractor.PositionHolder
;
import
com.google.android.exoplayer2.extractor.SeekMap
;
import
com.google.android.exoplayer2.extractor.ts.ElementaryStreamReader.TrackIdGenerator
;
import
com.google.android.exoplayer2.util.ParsableBitArray
;
import
com.google.android.exoplayer2.util.ParsableByteArray
;
import
com.google.android.exoplayer2.util.Util
;
...
...
@@ -126,7 +127,8 @@ public final class AdtsExtractor implements Extractor {
@Override
public
void
init
(
ExtractorOutput
output
)
{
reader
=
new
AdtsReader
(
output
.
track
(
0
),
output
.
track
(
1
));
reader
=
new
AdtsReader
(
true
);
reader
.
init
(
output
,
new
TrackIdGenerator
(
0
,
1
));
output
.
endTracks
();
output
.
seekMap
(
new
SeekMap
.
Unseekable
(
C
.
TIME_UNSET
));
}
...
...
library/src/main/java/com/google/android/exoplayer2/extractor/ts/AdtsReader.java
View file @
e2ff401e
...
...
@@ -19,6 +19,8 @@ import android.util.Log;
import
android.util.Pair
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.extractor.DummyTrackOutput
;
import
com.google.android.exoplayer2.extractor.ExtractorOutput
;
import
com.google.android.exoplayer2.extractor.TrackOutput
;
import
com.google.android.exoplayer2.util.CodecSpecificDataUtil
;
import
com.google.android.exoplayer2.util.MimeTypes
;
...
...
@@ -53,11 +55,14 @@ import java.util.Collections;
private
static
final
int
ID3_SIZE_OFFSET
=
6
;
private
static
final
byte
[]
ID3_IDENTIFIER
=
{
'I'
,
'D'
,
'3'
};
private
final
boolean
exposeId3
;
private
final
ParsableBitArray
adtsScratch
;
private
final
ParsableByteArray
id3HeaderBuffer
;
private
final
TrackOutput
id3Output
;
private
final
String
language
;
private
TrackOutput
output
;
private
TrackOutput
id3Output
;
private
int
state
;
private
int
bytesRead
;
...
...
@@ -77,26 +82,21 @@ import java.util.Collections;
private
long
currentSampleDuration
;
/**
* @param output A {@link TrackOutput} to which AAC samples should be written.
* @param id3Output A {@link TrackOutput} to which ID3 samples should be written.
* @param exposeId3 True if the reader should expose ID3 information.
*/
public
AdtsReader
(
TrackOutput
output
,
TrackOutput
id3Output
)
{
this
(
output
,
id3Output
,
null
);
public
AdtsReader
(
boolean
exposeId3
)
{
this
(
exposeId3
,
null
);
}
/**
* @param output A {@link TrackOutput} to which AAC samples should be written.
* @param id3Output A {@link TrackOutput} to which ID3 samples should be written.
* @param exposeId3 True if the reader should expose ID3 information.
* @param language Track language.
*/
public
AdtsReader
(
TrackOutput
output
,
TrackOutput
id3Output
,
String
language
)
{
super
(
output
);
this
.
id3Output
=
id3Output
;
id3Output
.
format
(
Format
.
createSampleFormat
(
null
,
MimeTypes
.
APPLICATION_ID3
,
null
,
Format
.
NO_VALUE
,
null
));
public
AdtsReader
(
boolean
exposeId3
,
String
language
)
{
adtsScratch
=
new
ParsableBitArray
(
new
byte
[
HEADER_SIZE
+
CRC_SIZE
]);
id3HeaderBuffer
=
new
ParsableByteArray
(
Arrays
.
copyOf
(
ID3_IDENTIFIER
,
ID3_HEADER_SIZE
));
setFindingSampleState
();
this
.
exposeId3
=
exposeId3
;
this
.
language
=
language
;
}
...
...
@@ -106,6 +106,18 @@ import java.util.Collections;
}
@Override
public
void
init
(
ExtractorOutput
extractorOutput
,
TrackIdGenerator
idGenerator
)
{
output
=
extractorOutput
.
track
(
idGenerator
.
getNextId
());
if
(
exposeId3
)
{
id3Output
=
extractorOutput
.
track
(
idGenerator
.
getNextId
());
id3Output
.
format
(
Format
.
createSampleFormat
(
null
,
MimeTypes
.
APPLICATION_ID3
,
null
,
Format
.
NO_VALUE
,
null
));
}
else
{
id3Output
=
new
DummyTrackOutput
();
}
}
@Override
public
void
packetStarted
(
long
pesTimeUs
,
boolean
dataAlignmentIndicator
)
{
timeUs
=
pesTimeUs
;
}
...
...
library/src/main/java/com/google/android/exoplayer2/extractor/ts/DefaultStreamReaderFactory.java
View file @
e2ff401e
...
...
@@ -16,9 +16,7 @@
package
com
.
google
.
android
.
exoplayer2
.
extractor
.
ts
;
import
android.support.annotation.IntDef
;
import
android.util.SparseBooleanArray
;
import
com.google.android.exoplayer2.extractor.DummyTrackOutput
;
import
com.google.android.exoplayer2.extractor.ExtractorOutput
;
import
com.google.android.exoplayer2.extractor.ts.ElementaryStreamReader.EsInfo
;
import
java.lang.annotation.Retention
;
import
java.lang.annotation.RetentionPolicy
;
...
...
@@ -28,80 +26,54 @@ import java.lang.annotation.RetentionPolicy;
public
final
class
DefaultStreamReaderFactory
implements
ElementaryStreamReader
.
Factory
{
/**
* Flags controlling
what workarounds are enabled for elementary stream readers
.
* Flags controlling
elementary stream readers behaviour
.
*/
@Retention
(
RetentionPolicy
.
SOURCE
)
@IntDef
(
flag
=
true
,
value
=
{
WORKAROUND_ALLOW_NON_IDR_KEYFRAMES
,
WORKAROUND
_IGNORE_AAC_STREAM
,
WORKAROUND_IGNORE_H264_STREAM
,
WORKAROUND_DETECT_ACCESS_UNITS
,
WORKAROUND_MAP_BY_TYPE
})
public
@interface
Workaround
Flags
{
@IntDef
(
flag
=
true
,
value
=
{
FLAG_ALLOW_NON_IDR_KEYFRAMES
,
FLAG
_IGNORE_AAC_STREAM
,
FLAG_IGNORE_H264_STREAM
,
FLAG_DETECT_ACCESS_UNITS
})
public
@interface
Flags
{
}
public
static
final
int
WORKAROUND_ALLOW_NON_IDR_KEYFRAMES
=
1
;
public
static
final
int
WORKAROUND_IGNORE_AAC_STREAM
=
2
;
public
static
final
int
WORKAROUND_IGNORE_H264_STREAM
=
4
;
public
static
final
int
WORKAROUND_DETECT_ACCESS_UNITS
=
8
;
public
static
final
int
WORKAROUND_MAP_BY_TYPE
=
16
;
public
static
final
int
FLAG_ALLOW_NON_IDR_KEYFRAMES
=
1
;
public
static
final
int
FLAG_IGNORE_AAC_STREAM
=
2
;
public
static
final
int
FLAG_IGNORE_H264_STREAM
=
4
;
public
static
final
int
FLAG_DETECT_ACCESS_UNITS
=
8
;
private
static
final
int
BASE_EMBEDDED_TRACK_ID
=
0x2000
;
// 0xFF + 1.
private
final
SparseBooleanArray
trackIds
;
@WorkaroundFlags
private
final
int
workaroundFlags
;
private
Id3Reader
id3Reader
;
private
int
nextEmbeddedTrackId
=
BASE_EMBEDDED_TRACK_ID
;
@Flags
private
final
int
flags
;
public
DefaultStreamReaderFactory
()
{
this
(
0
);
}
public
DefaultStreamReaderFactory
(
int
workaroundFlags
)
{
trackIds
=
new
SparseBooleanArray
();
this
.
workaroundFlags
=
workaroundFlags
;
public
DefaultStreamReaderFactory
(
@Flags
int
flags
)
{
this
.
flags
=
flags
;
}
@Override
public
ElementaryStreamReader
onPmtEntry
(
int
pid
,
int
streamType
,
ElementaryStreamReader
.
EsInfo
esInfo
,
ExtractorOutput
output
)
{
if
((
workaroundFlags
&
WORKAROUND_MAP_BY_TYPE
)
!=
0
&&
id3Reader
==
null
)
{
// Setup an ID3 track regardless of whether there's a corresponding entry, in case one
// appears intermittently during playback. See b/20261500.
id3Reader
=
new
Id3Reader
(
output
.
track
(
TsExtractor
.
TS_STREAM_TYPE_ID3
));
}
int
trackId
=
(
workaroundFlags
&
WORKAROUND_MAP_BY_TYPE
)
!=
0
?
streamType
:
pid
;
if
(
trackIds
.
get
(
trackId
))
{
return
null
;
}
trackIds
.
put
(
trackId
,
true
);
public
ElementaryStreamReader
createStreamReader
(
int
streamType
,
EsInfo
esInfo
)
{
switch
(
streamType
)
{
case
TsExtractor
.
TS_STREAM_TYPE_MPA
:
case
TsExtractor
.
TS_STREAM_TYPE_MPA_LSF
:
return
new
MpegAudioReader
(
output
.
track
(
trackId
),
esInfo
.
language
);
return
new
MpegAudioReader
(
esInfo
.
language
);
case
TsExtractor
.
TS_STREAM_TYPE_AAC
:
return
(
workaroundFlags
&
WORKAROUND
_IGNORE_AAC_STREAM
)
!=
0
?
null
:
new
AdtsReader
(
output
.
track
(
trackId
),
new
DummyTrackOutput
()
,
esInfo
.
language
);
return
(
flags
&
FLAG
_IGNORE_AAC_STREAM
)
!=
0
?
null
:
new
AdtsReader
(
false
,
esInfo
.
language
);
case
TsExtractor
.
TS_STREAM_TYPE_AC3
:
case
TsExtractor
.
TS_STREAM_TYPE_E_AC3
:
return
new
Ac3Reader
(
output
.
track
(
trackId
),
esInfo
.
language
);
return
new
Ac3Reader
(
esInfo
.
language
);
case
TsExtractor
.
TS_STREAM_TYPE_DTS
:
case
TsExtractor
.
TS_STREAM_TYPE_HDMV_DTS
:
return
new
DtsReader
(
output
.
track
(
trackId
),
esInfo
.
language
);
return
new
DtsReader
(
esInfo
.
language
);
case
TsExtractor
.
TS_STREAM_TYPE_H262
:
return
new
H262Reader
(
output
.
track
(
trackId
)
);
return
new
H262Reader
();
case
TsExtractor
.
TS_STREAM_TYPE_H264
:
return
(
workaroundFlags
&
WORKAROUND_IGNORE_H264_STREAM
)
!=
0
?
null
:
new
H264Reader
(
output
.
track
(
trackId
),
new
SeiReader
(
output
.
track
(
nextEmbeddedTrackId
++)),
(
workaroundFlags
&
WORKAROUND_ALLOW_NON_IDR_KEYFRAMES
)
!=
0
,
(
workaroundFlags
&
WORKAROUND_DETECT_ACCESS_UNITS
)
!=
0
);
return
(
flags
&
FLAG_IGNORE_H264_STREAM
)
!=
0
?
null
:
new
H264Reader
((
flags
&
FLAG_ALLOW_NON_IDR_KEYFRAMES
)
!=
0
,
(
flags
&
FLAG_DETECT_ACCESS_UNITS
)
!=
0
);
case
TsExtractor
.
TS_STREAM_TYPE_H265
:
return
new
H265Reader
(
output
.
track
(
trackId
),
new
SeiReader
(
output
.
track
(
nextEmbeddedTrackId
++)));
return
new
H265Reader
();
case
TsExtractor
.
TS_STREAM_TYPE_ID3
:
if
((
workaroundFlags
&
WORKAROUND_MAP_BY_TYPE
)
!=
0
)
{
return
id3Reader
;
}
else
{
return
new
Id3Reader
(
output
.
track
(
nextEmbeddedTrackId
++));
}
return
new
Id3Reader
();
default
:
return
null
;
}
...
...
library/src/main/java/com/google/android/exoplayer2/extractor/ts/DtsReader.java
View file @
e2ff401e
...
...
@@ -18,6 +18,7 @@ package com.google.android.exoplayer2.extractor.ts;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.audio.DtsUtil
;
import
com.google.android.exoplayer2.extractor.ExtractorOutput
;
import
com.google.android.exoplayer2.extractor.TrackOutput
;
import
com.google.android.exoplayer2.util.ParsableByteArray
;
...
...
@@ -37,6 +38,8 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
private
final
ParsableByteArray
headerScratchBytes
;
private
final
String
language
;
private
TrackOutput
output
;
private
int
state
;
private
int
bytesRead
;
...
...
@@ -54,20 +57,9 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
/**
* Constructs a new reader for DTS elementary streams.
*
* @param output Track output for extracted samples.
*/
public
DtsReader
(
TrackOutput
output
)
{
this
(
output
,
null
);
}
/**
* Constructs a new reader for DTS elementary streams.
*
* @param output Track output for extracted samples.
* @param language Track language.
*/
public
DtsReader
(
TrackOutput
output
,
String
language
)
{
super
(
output
);
public
DtsReader
(
String
language
)
{
headerScratchBytes
=
new
ParsableByteArray
(
new
byte
[
HEADER_SIZE
]);
headerScratchBytes
.
data
[
0
]
=
(
byte
)
((
SYNC_VALUE
>>
24
)
&
0xFF
);
headerScratchBytes
.
data
[
1
]
=
(
byte
)
((
SYNC_VALUE
>>
16
)
&
0xFF
);
...
...
@@ -85,6 +77,11 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
}
@Override
public
void
init
(
ExtractorOutput
extractorOutput
,
TrackIdGenerator
idGenerator
)
{
output
=
extractorOutput
.
track
(
idGenerator
.
getNextId
());
}
@Override
public
void
packetStarted
(
long
pesTimeUs
,
boolean
dataAlignmentIndicator
)
{
timeUs
=
pesTimeUs
;
}
...
...
library/src/main/java/com/google/android/exoplayer2/extractor/ts/ElementaryStreamReader.java
View file @
e2ff401e
...
...
@@ -33,17 +33,12 @@ public abstract class ElementaryStreamReader {
* Returns an {@link ElementaryStreamReader} for a given PMT entry. May return null if the
* stream type is not supported or if the stream already has a reader assigned to it.
*
* @param pid The pid for the PMT entry.
* @param streamType One of the {@link TsExtractor}{@code .TS_STREAM_TYPE_*} constants defining
* the type of the stream.
* @param esInfo The descriptor information linked to the elementary stream.
* @param output The {@link ExtractorOutput} that provides the {@link TrackOutput}s for the
* created readers.
* @param streamType Stream type value as defined in the PMT entry or associated descriptors.
* @param esInfo Information associated to the elementary stream provided in the PMT.
* @return An {@link ElementaryStreamReader} for the elementary streams carried by the provided
* pid. {@code null} if the stream is not supported or if it should be ignored.
*/
ElementaryStreamReader
onPmtEntry
(
int
pid
,
int
streamType
,
EsInfo
esInfo
,
ExtractorOutput
output
);
ElementaryStreamReader
createStreamReader
(
int
streamType
,
EsInfo
esInfo
);
}
...
...
@@ -70,13 +65,24 @@ public abstract class ElementaryStreamReader {
}
protected
final
TrackOutput
output
;
/**
*
@param output A {@link TrackOutput} to which samples should be written
.
*
Generates track ids for initializing {@link ElementaryStreamReader}s' {@link TrackOutput}s
.
*/
protected
ElementaryStreamReader
(
TrackOutput
output
)
{
this
.
output
=
output
;
public
static
final
class
TrackIdGenerator
{
private
final
int
firstId
;
private
final
int
idIncrement
;
private
int
generatedIdCount
;
public
TrackIdGenerator
(
int
firstId
,
int
idIncrement
)
{
this
.
firstId
=
firstId
;
this
.
idIncrement
=
idIncrement
;
}
public
int
getNextId
()
{
return
firstId
+
idIncrement
*
generatedIdCount
++;
}
}
/**
...
...
@@ -85,6 +91,15 @@ public abstract class ElementaryStreamReader {
public
abstract
void
seek
();
/**
* Initializes the reader by providing outputs and ids for the tracks.
*
* @param extractorOutput The {@link ExtractorOutput} that receives the extracted data.
* @param idGenerator A {@link TrackIdGenerator} that generates unique track ids for the
* {@link TrackOutput}s.
*/
public
abstract
void
init
(
ExtractorOutput
extractorOutput
,
TrackIdGenerator
idGenerator
);
/**
* Called when a packet starts.
*
* @param pesTimeUs The timestamp associated with the packet.
...
...
library/src/main/java/com/google/android/exoplayer2/extractor/ts/H262Reader.java
View file @
e2ff401e
...
...
@@ -18,6 +18,7 @@ package com.google.android.exoplayer2.extractor.ts;
import
android.util.Pair
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.extractor.ExtractorOutput
;
import
com.google.android.exoplayer2.extractor.TrackOutput
;
import
com.google.android.exoplayer2.util.MimeTypes
;
import
com.google.android.exoplayer2.util.NalUnitUtil
;
...
...
@@ -35,6 +36,8 @@ import java.util.Collections;
private
static
final
int
START_EXTENSION
=
0xB5
;
private
static
final
int
START_GROUP
=
0xB8
;
private
TrackOutput
output
;
// Maps (frame_rate_code - 1) indices to values, as defined in ITU-T H.262 Table 6-4.
private
static
final
double
[]
FRAME_RATE_VALUES
=
new
double
[]
{
24000
d
/
1001
,
24
,
25
,
30000
d
/
1001
,
30
,
50
,
60000
d
/
1001
,
60
};
...
...
@@ -58,8 +61,7 @@ import java.util.Collections;
private
long
framePosition
;
private
long
frameTimeUs
;
public
H262Reader
(
TrackOutput
output
)
{
super
(
output
);
public
H262Reader
()
{
prefixFlags
=
new
boolean
[
4
];
csdBuffer
=
new
CsdBuffer
(
128
);
}
...
...
@@ -74,6 +76,11 @@ import java.util.Collections;
}
@Override
public
void
init
(
ExtractorOutput
extractorOutput
,
TrackIdGenerator
idGenerator
)
{
output
=
extractorOutput
.
track
(
idGenerator
.
getNextId
());
}
@Override
public
void
packetStarted
(
long
pesTimeUs
,
boolean
dataAlignmentIndicator
)
{
pesPtsUsAvailable
=
pesTimeUs
!=
C
.
TIME_UNSET
;
if
(
pesPtsUsAvailable
)
{
...
...
library/src/main/java/com/google/android/exoplayer2/extractor/ts/H264Reader.java
View file @
e2ff401e
...
...
@@ -18,6 +18,7 @@ package com.google.android.exoplayer2.extractor.ts;
import
android.util.SparseArray
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.extractor.ExtractorOutput
;
import
com.google.android.exoplayer2.extractor.TrackOutput
;
import
com.google.android.exoplayer2.util.MimeTypes
;
import
com.google.android.exoplayer2.util.NalUnitUtil
;
...
...
@@ -37,17 +38,20 @@ import java.util.List;
private
static
final
int
NAL_UNIT_TYPE_SPS
=
7
;
// Sequence parameter set
private
static
final
int
NAL_UNIT_TYPE_PPS
=
8
;
// Picture parameter set
// State that should not be reset on seek.
private
boolean
hasOutputFormat
;
// State that should be reset on seek.
private
final
SeiReader
seiReader
;
private
final
boolean
[]
prefixFlags
;
private
final
SampleReader
sampleReader
;
private
final
boolean
allowNonIdrKeyframes
;
private
final
boolean
detectAccessUnits
;
private
final
NalUnitTargetBuffer
sps
;
private
final
NalUnitTargetBuffer
pps
;
private
final
NalUnitTargetBuffer
sei
;
private
long
totalBytesWritten
;
private
final
boolean
[]
prefixFlags
;
private
TrackOutput
output
;
private
SeiReader
seiReader
;
private
SampleReader
sampleReader
;
// State that should not be reset on seek.
private
boolean
hasOutputFormat
;
// Per packet state that gets reset at the start of each packet.
private
long
pesTimeUs
;
...
...
@@ -56,19 +60,15 @@ import java.util.List;
private
final
ParsableByteArray
seiWrapper
;
/**
* @param output A {@link TrackOutput} to which H.264 samples should be written.
* @param seiReader A reader for CEA-608 samples in SEI NAL units.
* @param allowNonIdrKeyframes Whether to treat samples consisting of non-IDR I slices as
* synchronization samples (key-frames).
* @param detectAccessUnits Whether to split the input stream into access units (samples) based on
* slice headers. Pass {@code false} if the stream contains access unit delimiters (AUDs).
*/
public
H264Reader
(
TrackOutput
output
,
SeiReader
seiReader
,
boolean
allowNonIdrKeyframes
,
boolean
detectAccessUnits
)
{
super
(
output
);
this
.
seiReader
=
seiReader
;
public
H264Reader
(
boolean
allowNonIdrKeyframes
,
boolean
detectAccessUnits
)
{
prefixFlags
=
new
boolean
[
3
];
sampleReader
=
new
SampleReader
(
output
,
allowNonIdrKeyframes
,
detectAccessUnits
);
this
.
allowNonIdrKeyframes
=
allowNonIdrKeyframes
;
this
.
detectAccessUnits
=
detectAccessUnits
;
sps
=
new
NalUnitTargetBuffer
(
NAL_UNIT_TYPE_SPS
,
128
);
pps
=
new
NalUnitTargetBuffer
(
NAL_UNIT_TYPE_PPS
,
128
);
sei
=
new
NalUnitTargetBuffer
(
NAL_UNIT_TYPE_SEI
,
128
);
...
...
@@ -86,6 +86,13 @@ import java.util.List;
}
@Override
public
void
init
(
ExtractorOutput
extractorOutput
,
TrackIdGenerator
idGenerator
)
{
output
=
extractorOutput
.
track
(
idGenerator
.
getNextId
());
sampleReader
=
new
SampleReader
(
output
,
allowNonIdrKeyframes
,
detectAccessUnits
);
seiReader
=
new
SeiReader
(
extractorOutput
.
track
(
idGenerator
.
getNextId
()));
}
@Override
public
void
packetStarted
(
long
pesTimeUs
,
boolean
dataAlignmentIndicator
)
{
this
.
pesTimeUs
=
pesTimeUs
;
}
...
...
library/src/main/java/com/google/android/exoplayer2/extractor/ts/H265Reader.java
View file @
e2ff401e
...
...
@@ -18,6 +18,7 @@ package com.google.android.exoplayer2.extractor.ts;
import
android.util.Log
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.extractor.ExtractorOutput
;
import
com.google.android.exoplayer2.extractor.TrackOutput
;
import
com.google.android.exoplayer2.util.MimeTypes
;
import
com.google.android.exoplayer2.util.NalUnitUtil
;
...
...
@@ -42,11 +43,13 @@ import java.util.Collections;
private
static
final
int
PREFIX_SEI_NUT
=
39
;
private
static
final
int
SUFFIX_SEI_NUT
=
40
;
private
TrackOutput
output
;
private
SeiReader
seiReader
;
// State that should not be reset on seek.
private
boolean
hasOutputFormat
;
// State that should be reset on seek.
private
final
SeiReader
seiReader
;
private
final
boolean
[]
prefixFlags
;
private
final
NalUnitTargetBuffer
vps
;
private
final
NalUnitTargetBuffer
sps
;
...
...
@@ -62,13 +65,7 @@ import java.util.Collections;
// Scratch variables to avoid allocations.
private
final
ParsableByteArray
seiWrapper
;
/**
* @param output A {@link TrackOutput} to which H.265 samples should be written.
* @param seiReader A reader for CEA-608 samples in SEI NAL units.
*/
public
H265Reader
(
TrackOutput
output
,
SeiReader
seiReader
)
{
super
(
output
);
this
.
seiReader
=
seiReader
;
public
H265Reader
()
{
prefixFlags
=
new
boolean
[
3
];
vps
=
new
NalUnitTargetBuffer
(
VPS_NUT
,
128
);
sps
=
new
NalUnitTargetBuffer
(
SPS_NUT
,
128
);
...
...
@@ -92,6 +89,12 @@ import java.util.Collections;
}
@Override
public
void
init
(
ExtractorOutput
extractorOutput
,
TrackIdGenerator
idGenerator
)
{
output
=
extractorOutput
.
track
(
idGenerator
.
getNextId
());
seiReader
=
new
SeiReader
(
extractorOutput
.
track
(
idGenerator
.
getNextId
()));
}
@Override
public
void
packetStarted
(
long
pesTimeUs
,
boolean
dataAlignmentIndicator
)
{
this
.
pesTimeUs
=
pesTimeUs
;
}
...
...
library/src/main/java/com/google/android/exoplayer2/extractor/ts/Id3Reader.java
View file @
e2ff401e
...
...
@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.extractor.ts;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.extractor.ExtractorOutput
;
import
com.google.android.exoplayer2.extractor.TrackOutput
;
import
com.google.android.exoplayer2.util.MimeTypes
;
import
com.google.android.exoplayer2.util.ParsableByteArray
;
...
...
@@ -30,6 +31,8 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
private
final
ParsableByteArray
id3Header
;
private
TrackOutput
output
;
// State that should be reset on seek.
private
boolean
writingSample
;
...
...
@@ -38,10 +41,7 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
private
int
sampleSize
;
private
int
sampleBytesRead
;
public
Id3Reader
(
TrackOutput
output
)
{
super
(
output
);
output
.
format
(
Format
.
createSampleFormat
(
null
,
MimeTypes
.
APPLICATION_ID3
,
null
,
Format
.
NO_VALUE
,
null
));
public
Id3Reader
()
{
id3Header
=
new
ParsableByteArray
(
ID3_HEADER_SIZE
);
}
...
...
@@ -51,6 +51,13 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
}
@Override
public
void
init
(
ExtractorOutput
extractorOutput
,
TrackIdGenerator
idGenerator
)
{
output
=
extractorOutput
.
track
(
idGenerator
.
getNextId
());
output
.
format
(
Format
.
createSampleFormat
(
null
,
MimeTypes
.
APPLICATION_ID3
,
null
,
Format
.
NO_VALUE
,
null
));
}
@Override
public
void
packetStarted
(
long
pesTimeUs
,
boolean
dataAlignmentIndicator
)
{
if
(!
dataAlignmentIndicator
)
{
return
;
...
...
library/src/main/java/com/google/android/exoplayer2/extractor/ts/MpegAudioReader.java
View file @
e2ff401e
...
...
@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.extractor.ts;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.extractor.ExtractorOutput
;
import
com.google.android.exoplayer2.extractor.MpegAudioHeader
;
import
com.google.android.exoplayer2.extractor.TrackOutput
;
import
com.google.android.exoplayer2.util.ParsableByteArray
;
...
...
@@ -36,6 +37,8 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
private
final
MpegAudioHeader
header
;
private
final
String
language
;
private
TrackOutput
output
;
private
int
state
;
private
int
frameBytesRead
;
private
boolean
hasOutputFormat
;
...
...
@@ -50,12 +53,11 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
// The timestamp to attach to the next sample in the current packet.
private
long
timeUs
;
public
MpegAudioReader
(
TrackOutput
output
)
{
this
(
output
,
null
);
public
MpegAudioReader
()
{
this
(
null
);
}
public
MpegAudioReader
(
TrackOutput
output
,
String
language
)
{
super
(
output
);
public
MpegAudioReader
(
String
language
)
{
state
=
STATE_FINDING_HEADER
;
// The first byte of an MPEG Audio frame header is always 0xFF.
headerScratch
=
new
ParsableByteArray
(
4
);
...
...
@@ -72,6 +74,11 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
}
@Override
public
void
init
(
ExtractorOutput
extractorOutput
,
TrackIdGenerator
idGenerator
)
{
output
=
extractorOutput
.
track
(
idGenerator
.
getNextId
());
}
@Override
public
void
packetStarted
(
long
pesTimeUs
,
boolean
dataAlignmentIndicator
)
{
timeUs
=
pesTimeUs
;
}
...
...
library/src/main/java/com/google/android/exoplayer2/extractor/ts/PsExtractor.java
View file @
e2ff401e
...
...
@@ -24,6 +24,7 @@ import com.google.android.exoplayer2.extractor.ExtractorsFactory;
import
com.google.android.exoplayer2.extractor.PositionHolder
;
import
com.google.android.exoplayer2.extractor.SeekMap
;
import
com.google.android.exoplayer2.extractor.TimestampAdjuster
;
import
com.google.android.exoplayer2.extractor.ts.ElementaryStreamReader.TrackIdGenerator
;
import
com.google.android.exoplayer2.util.ParsableBitArray
;
import
com.google.android.exoplayer2.util.ParsableByteArray
;
import
java.io.IOException
;
...
...
@@ -49,6 +50,7 @@ public final class PsExtractor implements Extractor {
private
static
final
int
SYSTEM_HEADER_START_CODE
=
0x000001BB
;
private
static
final
int
PACKET_START_CODE_PREFIX
=
0x000001
;
private
static
final
int
MPEG_PROGRAM_END_CODE
=
0x000001B9
;
private
static
final
int
MAX_STREAM_ID_PLUS_ONE
=
0x100
;
private
static
final
long
MAX_SEARCH_LENGTH
=
1024
*
1024
;
public
static
final
int
PRIVATE_STREAM_1
=
0xBD
;
...
...
@@ -189,16 +191,18 @@ public final class PsExtractor implements Extractor {
// Private stream, used for AC3 audio.
// NOTE: This may need further parsing to determine if its DTS, but that's likely only
// valid for DVDs.
elementaryStreamReader
=
new
Ac3Reader
(
output
.
track
(
streamId
)
);
elementaryStreamReader
=
new
Ac3Reader
();
foundAudioTrack
=
true
;
}
else
if
(!
foundAudioTrack
&&
(
streamId
&
AUDIO_STREAM_MASK
)
==
AUDIO_STREAM
)
{
elementaryStreamReader
=
new
MpegAudioReader
(
output
.
track
(
streamId
)
);
elementaryStreamReader
=
new
MpegAudioReader
();
foundAudioTrack
=
true
;
}
else
if
(!
foundVideoTrack
&&
(
streamId
&
VIDEO_STREAM_MASK
)
==
VIDEO_STREAM
)
{
elementaryStreamReader
=
new
H262Reader
(
output
.
track
(
streamId
)
);
elementaryStreamReader
=
new
H262Reader
();
foundVideoTrack
=
true
;
}
if
(
elementaryStreamReader
!=
null
)
{
TrackIdGenerator
idGenerator
=
new
TrackIdGenerator
(
streamId
,
MAX_STREAM_ID_PLUS_ONE
);
elementaryStreamReader
.
init
(
output
,
idGenerator
);
payloadReader
=
new
PesReader
(
elementaryStreamReader
,
timestampAdjuster
);
psPayloadReaders
.
put
(
streamId
,
payloadReader
);
}
...
...
library/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java
View file @
e2ff401e
...
...
@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.extractor.ts;
import
android.util.Log
;
import
android.util.SparseArray
;
import
android.util.SparseBooleanArray
;
import
android.util.SparseIntArray
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.extractor.Extractor
;
...
...
@@ -26,6 +27,9 @@ import com.google.android.exoplayer2.extractor.ExtractorsFactory;
import
com.google.android.exoplayer2.extractor.PositionHolder
;
import
com.google.android.exoplayer2.extractor.SeekMap
;
import
com.google.android.exoplayer2.extractor.TimestampAdjuster
;
import
com.google.android.exoplayer2.extractor.TrackOutput
;
import
com.google.android.exoplayer2.extractor.ts.ElementaryStreamReader.EsInfo
;
import
com.google.android.exoplayer2.extractor.ts.ElementaryStreamReader.TrackIdGenerator
;
import
com.google.android.exoplayer2.util.Assertions
;
import
com.google.android.exoplayer2.util.ParsableBitArray
;
import
com.google.android.exoplayer2.util.ParsableByteArray
;
...
...
@@ -50,12 +54,6 @@ public final class TsExtractor implements Extractor {
};
private
static
final
String
TAG
=
"TsExtractor"
;
private
static
final
int
TS_PACKET_SIZE
=
188
;
private
static
final
int
TS_SYNC_BYTE
=
0x47
;
// First byte of each TS packet.
private
static
final
int
TS_PAT_PID
=
0
;
public
static
final
int
TS_STREAM_TYPE_MPA
=
0x03
;
public
static
final
int
TS_STREAM_TYPE_MPA_LSF
=
0x04
;
public
static
final
int
TS_STREAM_TYPE_AAC
=
0x0F
;
...
...
@@ -68,6 +66,12 @@ public final class TsExtractor implements Extractor {
public
static
final
int
TS_STREAM_TYPE_H265
=
0x24
;
public
static
final
int
TS_STREAM_TYPE_ID3
=
0x15
;
private
static
final
String
TAG
=
"TsExtractor"
;
private
static
final
int
TS_PACKET_SIZE
=
188
;
private
static
final
int
TS_SYNC_BYTE
=
0x47
;
// First byte of each TS packet.
private
static
final
int
TS_PAT_PID
=
0
;
private
static
final
int
MAX_PID_PLUS_ONE
=
0x2000
;
private
static
final
long
AC3_FORMAT_IDENTIFIER
=
Util
.
getIntegerCodeForString
(
"AC-3"
);
private
static
final
long
E_AC3_FORMAT_IDENTIFIER
=
Util
.
getIntegerCodeForString
(
"EAC3"
);
...
...
@@ -76,15 +80,19 @@ public final class TsExtractor implements Extractor {
private
static
final
int
BUFFER_PACKET_COUNT
=
5
;
// Should be at least 2
private
static
final
int
BUFFER_SIZE
=
TS_PACKET_SIZE
*
BUFFER_PACKET_COUNT
;
private
final
boolean
mapByType
;
private
final
TimestampAdjuster
timestampAdjuster
;
private
final
ParsableByteArray
tsPacketBuffer
;
private
final
ParsableBitArray
tsScratch
;
private
final
SparseIntArray
continuityCounters
;
private
final
ElementaryStreamReader
.
Factory
streamReaderFactory
;
/* package */
final
SparseArray
<
TsPayloadReader
>
tsPayloadReaders
;
// Indexed by pid
private
final
SparseArray
<
TsPayloadReader
>
tsPayloadReaders
;
// Indexed by pid
private
final
SparseBooleanArray
trackIds
;
// Accessed only by the loading thread.
private
ExtractorOutput
output
;
private
boolean
tracksEnded
;
private
ElementaryStreamReader
id3Reader
;
public
TsExtractor
()
{
this
(
new
TimestampAdjuster
(
0
));
...
...
@@ -94,22 +102,26 @@ public final class TsExtractor implements Extractor {
* @param timestampAdjuster A timestamp adjuster for offsetting and scaling sample timestamps.
*/
public
TsExtractor
(
TimestampAdjuster
timestampAdjuster
)
{
this
(
timestampAdjuster
,
new
DefaultStreamReaderFactory
());
this
(
timestampAdjuster
,
new
DefaultStreamReaderFactory
()
,
false
);
}
/**
* @param timestampAdjuster A timestamp adjuster for offsetting and scaling sample timestamps.
* @param customReaderFactory Factory for injecting a custom set of elementary stream readers.
* @param mapByType True if {@link TrackOutput}s should be mapped by their type, false to map them
* by their PID.
*/
public
TsExtractor
(
TimestampAdjuster
timestampAdjuster
,
ElementaryStreamReader
.
Factory
customReaderFactory
)
{
ElementaryStreamReader
.
Factory
customReaderFactory
,
boolean
mapByType
)
{
this
.
timestampAdjuster
=
timestampAdjuster
;
this
.
streamReaderFactory
=
Assertions
.
checkNotNull
(
customReaderFactory
);
this
.
mapByType
=
mapByType
;
tsPacketBuffer
=
new
ParsableByteArray
(
BUFFER_SIZE
);
tsScratch
=
new
ParsableBitArray
(
new
byte
[
3
]);
trackIds
=
new
SparseBooleanArray
();
tsPayloadReaders
=
new
SparseArray
<>();
tsPayloadReaders
.
put
(
TS_PAT_PID
,
new
PatReader
());
continuityCounters
=
new
SparseIntArray
();
resetPayloadReaders
();
}
// Extractor implementation.
...
...
@@ -141,11 +153,10 @@ public final class TsExtractor implements Extractor {
@Override
public
void
seek
(
long
position
)
{
timestampAdjuster
.
reset
();
for
(
int
i
=
0
;
i
<
tsPayloadReaders
.
size
();
i
++)
{
tsPayloadReaders
.
valueAt
(
i
).
seek
();
}
tsPacketBuffer
.
reset
();
continuityCounters
.
clear
();
// Elementary stream readers' state should be cleared to get consistent behaviours when seeking.
resetPayloadReaders
();
}
@Override
...
...
@@ -240,6 +251,13 @@ public final class TsExtractor implements Extractor {
// Internals.
private
void
resetPayloadReaders
()
{
trackIds
.
clear
();
tsPayloadReaders
.
clear
();
tsPayloadReaders
.
put
(
TS_PAT_PID
,
new
PatReader
());
id3Reader
=
null
;
}
/**
* Parses TS packet payload data.
*/
...
...
@@ -333,7 +351,7 @@ public final class TsExtractor implements Extractor {
patScratch
.
skipBits
(
13
);
// network_PID (13)
}
else
{
int
pid
=
patScratch
.
readBits
(
13
);
tsPayloadReaders
.
put
(
pid
,
new
PmtReader
());
tsPayloadReaders
.
put
(
pid
,
new
PmtReader
(
pid
));
}
}
}
...
...
@@ -353,14 +371,16 @@ public final class TsExtractor implements Extractor {
private
final
ParsableBitArray
pmtScratch
;
private
final
ParsableByteArray
sectionData
;
private
final
int
pid
;
private
int
sectionLength
;
private
int
sectionBytesRead
;
private
int
crc
;
public
PmtReader
()
{
public
PmtReader
(
int
pid
)
{
pmtScratch
=
new
ParsableBitArray
(
new
byte
[
5
]);
sectionData
=
new
ParsableByteArray
();
this
.
pid
=
pid
;
}
@Override
...
...
@@ -413,6 +433,14 @@ public final class TsExtractor implements Extractor {
// Skip the descriptors.
sectionData
.
skipBytes
(
programInfoLength
);
if
(
mapByType
&&
id3Reader
==
null
)
{
// Setup an ID3 track regardless of whether there's a corresponding entry, in case one
// appears intermittently during playback. See [Internal: b/20261500].
EsInfo
dummyEsInfo
=
new
EsInfo
(
TS_STREAM_TYPE_ID3
,
null
,
new
byte
[
0
]);
id3Reader
=
streamReaderFactory
.
createStreamReader
(
TS_STREAM_TYPE_ID3
,
dummyEsInfo
);
id3Reader
.
init
(
output
,
new
TrackIdGenerator
(
TS_STREAM_TYPE_ID3
,
MAX_PID_PLUS_ONE
));
}
int
remainingEntriesLength
=
sectionLength
-
9
/* Length of fields before descriptors */
-
programInfoLength
-
4
/* CRC length */
;
while
(
remainingEntriesLength
>
0
)
{
...
...
@@ -422,21 +450,40 @@ public final class TsExtractor implements Extractor {
int
elementaryPid
=
pmtScratch
.
readBits
(
13
);
pmtScratch
.
skipBits
(
4
);
// reserved
int
esInfoLength
=
pmtScratch
.
readBits
(
12
);
// ES_info_length.
E
lementaryStreamReader
.
E
sInfo
esInfo
=
readEsInfo
(
sectionData
,
esInfoLength
);
EsInfo
esInfo
=
readEsInfo
(
sectionData
,
esInfoLength
);
if
(
streamType
==
0x06
)
{
streamType
=
esInfo
.
streamType
;
}
remainingEntriesLength
-=
esInfoLength
+
5
;
ElementaryStreamReader
pesPayloadReader
=
streamReaderFactory
.
onPmtEntry
(
elementaryPid
,
streamType
,
esInfo
,
output
);
int
trackId
=
mapByType
?
streamType
:
elementaryPid
;
if
(
trackIds
.
get
(
trackId
))
{
continue
;
}
trackIds
.
put
(
trackId
,
true
);
ElementaryStreamReader
pesPayloadReader
;
if
(
mapByType
&&
streamType
==
TS_STREAM_TYPE_ID3
)
{
pesPayloadReader
=
id3Reader
;
}
else
{
pesPayloadReader
=
streamReaderFactory
.
createStreamReader
(
streamType
,
esInfo
);
pesPayloadReader
.
init
(
output
,
new
TrackIdGenerator
(
trackId
,
MAX_PID_PLUS_ONE
));
}
if
(
pesPayloadReader
!=
null
)
{
tsPayloadReaders
.
put
(
elementaryPid
,
new
PesReader
(
pesPayloadReader
,
timestampAdjuster
));
tsPayloadReaders
.
put
(
elementaryPid
,
new
PesReader
(
pesPayloadReader
,
timestampAdjuster
));
}
}
output
.
endTracks
();
if
(
mapByType
)
{
if
(!
tracksEnded
)
{
output
.
endTracks
();
}
}
else
{
tsPayloadReaders
.
remove
(
TS_PAT_PID
);
tsPayloadReaders
.
remove
(
pid
);
output
.
endTracks
();
}
tracksEnded
=
true
;
}
/**
...
...
@@ -447,7 +494,7 @@ public final class TsExtractor implements Extractor {
* @param length The length of descriptors to read from the current position in {@code data}.
* @return The stream info read from the available descriptors.
*/
private
E
lementaryStreamReader
.
E
sInfo
readEsInfo
(
ParsableByteArray
data
,
int
length
)
{
private
EsInfo
readEsInfo
(
ParsableByteArray
data
,
int
length
)
{
int
descriptorsStartPosition
=
data
.
getPosition
();
int
descriptorsEndPosition
=
descriptorsStartPosition
+
length
;
int
streamType
=
-
1
;
...
...
@@ -479,7 +526,7 @@ public final class TsExtractor implements Extractor {
data
.
skipBytes
(
positionOfNextDescriptor
-
data
.
getPosition
());
}
data
.
setPosition
(
descriptorsEndPosition
);
return
new
E
lementaryStreamReader
.
E
sInfo
(
streamType
,
language
,
return
new
EsInfo
(
streamType
,
language
,
Arrays
.
copyOfRange
(
sectionData
.
data
,
descriptorsStartPosition
,
descriptorsEndPosition
));
}
...
...
library/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java
View file @
e2ff401e
...
...
@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.source;
import
android.net.Uri
;
import
android.os.Handler
;
import
android.util.SparseArray
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.FormatHolder
;
...
...
@@ -41,7 +42,6 @@ import com.google.android.exoplayer2.util.ConditionVariable;
import
com.google.android.exoplayer2.util.Util
;
import
java.io.EOFException
;
import
java.io.IOException
;
import
java.util.Arrays
;
/**
* A {@link MediaPeriod} that extracts data using an {@link Extractor}.
...
...
@@ -68,6 +68,7 @@ import java.util.Arrays;
private
final
Runnable
maybeFinishPrepareRunnable
;
private
final
Runnable
onContinueLoadingRequestedRunnable
;
private
final
Handler
handler
;
private
final
SparseArray
<
DefaultTrackOutput
>
sampleQueues
;
private
Callback
callback
;
private
SeekMap
seekMap
;
...
...
@@ -77,7 +78,6 @@ import java.util.Arrays;
private
boolean
seenFirstTrackSelection
;
private
boolean
notifyReset
;
private
int
enabledTrackCount
;
private
DefaultTrackOutput
[]
sampleQueues
;
private
TrackGroupArray
tracks
;
private
long
durationUs
;
private
boolean
[]
trackEnabledStates
;
...
...
@@ -131,7 +131,7 @@ import java.util.Arrays;
handler
=
new
Handler
();
pendingResetPositionUs
=
C
.
TIME_UNSET
;
sampleQueues
=
new
DefaultTrackOutput
[
0
]
;
sampleQueues
=
new
SparseArray
<>()
;
length
=
C
.
LENGTH_UNSET
;
}
...
...
@@ -141,8 +141,9 @@ import java.util.Arrays;
@Override
public
void
run
()
{
extractorHolder
.
release
();
for
(
DefaultTrackOutput
sampleQueue
:
sampleQueues
)
{
sampleQueue
.
disable
();
int
trackCount
=
sampleQueues
.
size
();
for
(
int
i
=
0
;
i
<
trackCount
;
i
++)
{
sampleQueues
.
valueAt
(
i
).
disable
();
}
}
});
...
...
@@ -178,7 +179,7 @@ import java.util.Arrays;
Assertions
.
checkState
(
trackEnabledStates
[
track
]);
enabledTrackCount
--;
trackEnabledStates
[
track
]
=
false
;
sampleQueues
[
track
]
.
disable
();
sampleQueues
.
valueAt
(
track
)
.
disable
();
streams
[
i
]
=
null
;
}
}
...
...
@@ -201,9 +202,10 @@ import java.util.Arrays;
if
(!
seenFirstTrackSelection
)
{
// At the time of the first track selection all queues will be enabled, so we need to disable
// any that are no longer required.
for
(
int
i
=
0
;
i
<
sampleQueues
.
length
;
i
++)
{
int
trackCount
=
sampleQueues
.
size
();
for
(
int
i
=
0
;
i
<
trackCount
;
i
++)
{
if
(!
trackEnabledStates
[
i
])
{
sampleQueues
[
i
]
.
disable
();
sampleQueues
.
valueAt
(
i
)
.
disable
();
}
}
}
...
...
@@ -270,11 +272,12 @@ import java.util.Arrays;
// Treat all seeks into non-seekable media as being to t=0.
positionUs
=
seekMap
.
isSeekable
()
?
positionUs
:
0
;
lastSeekPositionUs
=
positionUs
;
int
trackCount
=
sampleQueues
.
size
();
// If we're not pending a reset, see if we can seek within the sample queues.
boolean
seekInsideBuffer
=
!
isPendingReset
();
for
(
int
i
=
0
;
seekInsideBuffer
&&
i
<
sampleQueues
.
length
;
i
++)
{
for
(
int
i
=
0
;
seekInsideBuffer
&&
i
<
trackCount
;
i
++)
{
if
(
trackEnabledStates
[
i
])
{
seekInsideBuffer
=
sampleQueues
[
i
]
.
skipToKeyframeBefore
(
positionUs
);
seekInsideBuffer
=
sampleQueues
.
valueAt
(
i
)
.
skipToKeyframeBefore
(
positionUs
);
}
}
// If we failed to seek within the sample queues, we need to restart.
...
...
@@ -284,8 +287,8 @@ import java.util.Arrays;
if
(
loader
.
isLoading
())
{
loader
.
cancelLoading
();
}
else
{
for
(
int
i
=
0
;
i
<
sampleQueues
.
length
;
i
++)
{
sampleQueues
[
i
]
.
reset
(
trackEnabledStates
[
i
]);
for
(
int
i
=
0
;
i
<
trackCount
;
i
++)
{
sampleQueues
.
valueAt
(
i
)
.
reset
(
trackEnabledStates
[
i
]);
}
}
}
...
...
@@ -296,7 +299,7 @@ import java.util.Arrays;
// SampleStream methods.
/* package */
boolean
isReady
(
int
track
)
{
return
loadingFinished
||
(!
isPendingReset
()
&&
!
sampleQueues
[
track
]
.
isEmpty
());
return
loadingFinished
||
(!
isPendingReset
()
&&
!
sampleQueues
.
valueAt
(
track
)
.
isEmpty
());
}
/* package */
void
maybeThrowError
()
throws
IOException
{
...
...
@@ -308,7 +311,8 @@ import java.util.Arrays;
return
C
.
RESULT_NOTHING_READ
;
}
return
sampleQueues
[
track
].
readData
(
formatHolder
,
buffer
,
loadingFinished
,
lastSeekPositionUs
);
return
sampleQueues
.
valueAt
(
track
).
readData
(
formatHolder
,
buffer
,
loadingFinished
,
lastSeekPositionUs
);
}
// Loader.Callback implementation.
...
...
@@ -332,8 +336,9 @@ import java.util.Arrays;
long
loadDurationMs
,
boolean
released
)
{
copyLengthFromLoader
(
loadable
);
if
(!
released
&&
enabledTrackCount
>
0
)
{
for
(
int
i
=
0
;
i
<
sampleQueues
.
length
;
i
++)
{
sampleQueues
[
i
].
reset
(
trackEnabledStates
[
i
]);
int
trackCount
=
sampleQueues
.
size
();
for
(
int
i
=
0
;
i
<
trackCount
;
i
++)
{
sampleQueues
.
valueAt
(
i
).
reset
(
trackEnabledStates
[
i
]);
}
callback
.
onContinueLoadingRequested
(
this
);
}
...
...
@@ -358,11 +363,13 @@ import java.util.Arrays;
@Override
public
TrackOutput
track
(
int
id
)
{
sampleQueues
=
Arrays
.
copyOf
(
sampleQueues
,
sampleQueues
.
length
+
1
);
DefaultTrackOutput
sampleQueue
=
new
DefaultTrackOutput
(
allocator
);
sampleQueue
.
setUpstreamFormatChangeListener
(
this
);
sampleQueues
[
sampleQueues
.
length
-
1
]
=
sampleQueue
;
return
sampleQueue
;
DefaultTrackOutput
trackOutput
=
sampleQueues
.
get
(
id
);
if
(
trackOutput
==
null
)
{
trackOutput
=
new
DefaultTrackOutput
(
allocator
);
trackOutput
.
setUpstreamFormatChangeListener
(
this
);
sampleQueues
.
put
(
id
,
trackOutput
);
}
return
trackOutput
;
}
@Override
...
...
@@ -390,18 +397,18 @@ import java.util.Arrays;
if
(
released
||
prepared
||
seekMap
==
null
||
!
tracksBuilt
)
{
return
;
}
for
(
DefaultTrackOutput
sampleQueue
:
sampleQueues
)
{
if
(
sampleQueue
.
getUpstreamFormat
()
==
null
)
{
int
trackCount
=
sampleQueues
.
size
();
for
(
int
i
=
0
;
i
<
trackCount
;
i
++)
{
if
(
sampleQueues
.
valueAt
(
i
).
getUpstreamFormat
()
==
null
)
{
return
;
}
}
loadCondition
.
close
();
int
trackCount
=
sampleQueues
.
length
;
TrackGroup
[]
trackArray
=
new
TrackGroup
[
trackCount
];
trackEnabledStates
=
new
boolean
[
trackCount
];
durationUs
=
seekMap
.
getDurationUs
();
for
(
int
i
=
0
;
i
<
trackCount
;
i
++)
{
trackArray
[
i
]
=
new
TrackGroup
(
sampleQueues
[
i
]
.
getUpstreamFormat
());
trackArray
[
i
]
=
new
TrackGroup
(
sampleQueues
.
valueAt
(
i
)
.
getUpstreamFormat
());
}
tracks
=
new
TrackGroupArray
(
trackArray
);
prepared
=
true
;
...
...
@@ -455,8 +462,9 @@ import java.util.Arrays;
// a new load.
lastSeekPositionUs
=
0
;
notifyReset
=
prepared
;
for
(
int
i
=
0
;
i
<
sampleQueues
.
length
;
i
++)
{
sampleQueues
[
i
].
reset
(!
prepared
||
trackEnabledStates
[
i
]);
int
trackCount
=
sampleQueues
.
size
();
for
(
int
i
=
0
;
i
<
trackCount
;
i
++)
{
sampleQueues
.
valueAt
(
i
).
reset
(!
prepared
||
trackEnabledStates
[
i
]);
}
loadable
.
setLoadPosition
(
0
);
}
...
...
@@ -464,17 +472,19 @@ import java.util.Arrays;
private
int
getExtractedSamplesCount
()
{
int
extractedSamplesCount
=
0
;
for
(
DefaultTrackOutput
sampleQueue
:
sampleQueues
)
{
extractedSamplesCount
+=
sampleQueue
.
getWriteIndex
();
int
trackCount
=
sampleQueues
.
size
();
for
(
int
i
=
0
;
i
<
trackCount
;
i
++)
{
extractedSamplesCount
+=
sampleQueues
.
valueAt
(
i
).
getWriteIndex
();
}
return
extractedSamplesCount
;
}
private
long
getLargestQueuedTimestampUs
()
{
long
largestQueuedTimestampUs
=
Long
.
MIN_VALUE
;
for
(
DefaultTrackOutput
sampleQueue
:
sampleQueues
)
{
int
trackCount
=
sampleQueues
.
size
();
for
(
int
i
=
0
;
i
<
trackCount
;
i
++)
{
largestQueuedTimestampUs
=
Math
.
max
(
largestQueuedTimestampUs
,
sampleQueue
.
getLargestQueuedTimestampUs
());
sampleQueue
s
.
valueAt
(
i
)
.
getLargestQueuedTimestampUs
());
}
return
largestQueuedTimestampUs
;
}
...
...
@@ -523,7 +533,7 @@ import java.util.Arrays;
@Override
public
void
skipToKeyframeBefore
(
long
timeUs
)
{
sampleQueues
[
track
]
.
skipToKeyframeBefore
(
timeUs
);
sampleQueues
.
valueAt
(
track
)
.
skipToKeyframeBefore
(
timeUs
);
}
}
...
...
library/src/main/java/com/google/android/exoplayer2/source/chunk/ChunkExtractorWrapper.java
View file @
e2ff401e
...
...
@@ -59,6 +59,7 @@ public final class ChunkExtractorWrapper implements ExtractorOutput, TrackOutput
// Accessed only on the loader thread.
private
boolean
seenTrack
;
private
int
seenTrackId
;
/**
* @param extractor The extractor to wrap.
...
...
@@ -116,8 +117,9 @@ public final class ChunkExtractorWrapper implements ExtractorOutput, TrackOutput
@Override
public
TrackOutput
track
(
int
id
)
{
Assertions
.
checkState
(!
seenTrack
);
Assertions
.
checkState
(!
seenTrack
||
seenTrackId
==
id
);
seenTrack
=
true
;
seenTrackId
=
id
;
return
this
;
}
...
...
library/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParser.java
View file @
e2ff401e
...
...
@@ -655,7 +655,9 @@ public class DashManifestParser extends DefaultHandler
return
MimeTypes
.
getVideoMediaMimeType
(
codecs
);
}
else
if
(
MimeTypes
.
APPLICATION_RAWCC
.
equals
(
containerMimeType
))
{
if
(
codecs
!=
null
)
{
if
(
codecs
.
contains
(
"eia608"
)
||
codecs
.
contains
(
"cea608"
))
{
if
(
codecs
.
contains
(
"cea708"
))
{
return
MimeTypes
.
APPLICATION_CEA708
;
}
else
if
(
codecs
.
contains
(
"eia608"
)
||
codecs
.
contains
(
"cea608"
))
{
return
MimeTypes
.
APPLICATION_CEA608
;
}
}
...
...
library/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java
View file @
e2ff401e
...
...
@@ -257,8 +257,16 @@ import java.util.Locale;
chunkMediaSequence
=
getLiveNextChunkSequenceNumber
(
previous
.
chunkIndex
,
oldVariantIndex
,
newVariantIndex
);
if
(
chunkMediaSequence
<
mediaPlaylist
.
mediaSequence
)
{
fatalError
=
new
BehindLiveWindowException
();
return
;
// We try getting the next chunk without adapting in case that's the reason for falling
// behind the live window.
newVariantIndex
=
oldVariantIndex
;
mediaPlaylist
=
variantPlaylists
[
newVariantIndex
];
chunkMediaSequence
=
getLiveNextChunkSequenceNumber
(
previous
.
chunkIndex
,
oldVariantIndex
,
newVariantIndex
);
if
(
chunkMediaSequence
<
mediaPlaylist
.
mediaSequence
)
{
fatalError
=
new
BehindLiveWindowException
();
return
;
}
}
}
}
else
{
...
...
@@ -369,29 +377,29 @@ import java.util.Locale;
}
}
else
if
(
needNewExtractor
)
{
// MPEG-2 TS segments, but we need a new extractor.
// This flag ensures the change of pid between streams does not affect the sample queues.
@DefaultStreamReaderFactory
.
WorkaroundFlags
int
workaroundFlags
=
DefaultStreamReaderFactory
.
WORKAROUND_MAP_BY_TYPE
;
String
codecs
=
variants
[
newVariantIndex
].
format
.
codecs
;
if
(!
TextUtils
.
isEmpty
(
codecs
))
{
// Sometimes AAC and H264 streams are declared in TS chunks even though they don't really
// exist. If we know from the codec attribute that they don't exist, then we can explicitly
// ignore them even if they're declared.
if
(!
MimeTypes
.
AUDIO_AAC
.
equals
(
MimeTypes
.
getAudioMediaMimeType
(
codecs
)))
{
workaroundFlags
|=
DefaultStreamReaderFactory
.
WORKAROUND_IGNORE_AAC_STREAM
;
}
if
(!
MimeTypes
.
VIDEO_H264
.
equals
(
MimeTypes
.
getVideoMediaMimeType
(
codecs
)))
{
workaroundFlags
|=
DefaultStreamReaderFactory
.
WORKAROUND_IGNORE_H264_STREAM
;
}
}
isTimestampMaster
=
true
;
if
(
useInitializedExtractor
)
{
extractor
=
lastLoadedInitializationChunk
.
extractor
;
}
else
{
timestampAdjuster
=
timestampAdjusterProvider
.
getAdjuster
(
segment
.
discontinuitySequenceNumber
,
startTimeUs
);
// This flag ensures the change of pid between streams does not affect the sample queues.
@DefaultStreamReaderFactory
.
Flags
int
esReaderFactoryFlags
=
0
;
String
codecs
=
variants
[
newVariantIndex
].
format
.
codecs
;
if
(!
TextUtils
.
isEmpty
(
codecs
))
{
// Sometimes AAC and H264 streams are declared in TS chunks even though they don't really
// exist. If we know from the codec attribute that they don't exist, then we can
// explicitly ignore them even if they're declared.
if
(!
MimeTypes
.
AUDIO_AAC
.
equals
(
MimeTypes
.
getAudioMediaMimeType
(
codecs
)))
{
esReaderFactoryFlags
|=
DefaultStreamReaderFactory
.
FLAG_IGNORE_AAC_STREAM
;
}
if
(!
MimeTypes
.
VIDEO_H264
.
equals
(
MimeTypes
.
getVideoMediaMimeType
(
codecs
)))
{
esReaderFactoryFlags
|=
DefaultStreamReaderFactory
.
FLAG_IGNORE_H264_STREAM
;
}
}
extractor
=
new
TsExtractor
(
timestampAdjuster
,
new
DefaultStreamReaderFactory
(
workaroundFlags
)
);
new
DefaultStreamReaderFactory
(
esReaderFactoryFlags
),
true
);
}
}
else
{
// MPEG-2 TS segments, and we need to continue using the same extractor.
...
...
library/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java
View file @
e2ff401e
...
...
@@ -328,7 +328,7 @@ import java.util.List;
sampleStreamWrappers
=
new
HlsSampleStreamWrapper
[]
{
buildSampleStreamWrapper
(
C
.
TRACK_TYPE_DEFAULT
,
baseUri
,
variants
,
null
,
null
)};
pendingPrepareCount
=
1
;
sampleStreamWrappers
[
0
].
prepare
();
sampleStreamWrappers
[
0
].
continuePreparing
();
return
;
}
...
...
@@ -369,16 +369,16 @@ import java.util.List;
selectedVariants
.
toArray
(
variants
);
HlsSampleStreamWrapper
sampleStreamWrapper
=
buildSampleStreamWrapper
(
C
.
TRACK_TYPE_DEFAULT
,
baseUri
,
variants
,
masterPlaylist
.
muxedAudioFormat
,
masterPlaylist
.
muxedCaptionFormat
);
sampleStreamWrapper
.
prepare
();
sampleStreamWrappers
[
currentWrapperIndex
++]
=
sampleStreamWrapper
;
sampleStreamWrapper
.
continuePreparing
();
}
// Build audio stream wrappers.
for
(
int
i
=
0
;
i
<
audioVariants
.
size
();
i
++)
{
HlsSampleStreamWrapper
sampleStreamWrapper
=
buildSampleStreamWrapper
(
C
.
TRACK_TYPE_AUDIO
,
baseUri
,
new
HlsMasterPlaylist
.
HlsUrl
[]
{
audioVariants
.
get
(
i
)},
null
,
null
);
sampleStreamWrapper
.
prepare
();
sampleStreamWrappers
[
currentWrapperIndex
++]
=
sampleStreamWrapper
;
sampleStreamWrapper
.
continuePreparing
();
}
// Build subtitle stream wrappers.
...
...
library/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java
View file @
e2ff401e
...
...
@@ -144,8 +144,10 @@ import java.util.LinkedList;
pendingResetPositionUs
=
positionUs
;
}
public
void
prepare
()
{
continueLoading
(
lastSeekPositionUs
);
public
void
continuePreparing
()
{
if
(!
prepared
)
{
continueLoading
(
lastSeekPositionUs
);
}
}
/**
...
...
@@ -154,7 +156,8 @@ import java.util.LinkedList;
*/
public
void
prepareSingleTrack
(
Format
format
)
{
track
(
0
).
format
(
format
);
endTracks
();
sampleQueuesBuilt
=
true
;
maybeFinishPrepare
();
}
public
void
maybeThrowPrepareError
()
throws
IOException
{
...
...
library/src/main/java/com/google/android/exoplayer2/upstream/DefaultHttpDataSource.java
View file @
e2ff401e
...
...
@@ -413,11 +413,16 @@ public class DefaultHttpDataSource implements HttpDataSource {
connection
.
setInstanceFollowRedirects
(
followRedirects
);
connection
.
setDoOutput
(
postBody
!=
null
);
if
(
postBody
!=
null
)
{
connection
.
setFixedLengthStreamingMode
(
postBody
.
length
);
connection
.
connect
();
OutputStream
os
=
connection
.
getOutputStream
();
os
.
write
(
postBody
);
os
.
close
();
connection
.
setRequestMethod
(
"POST"
);
if
(
postBody
.
length
==
0
)
{
connection
.
connect
();
}
else
{
connection
.
setFixedLengthStreamingMode
(
postBody
.
length
);
connection
.
connect
();
OutputStream
os
=
connection
.
getOutputStream
();
os
.
write
(
postBody
);
os
.
close
();
}
}
else
{
connection
.
connect
();
}
...
...
library/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java
View file @
e2ff401e
...
...
@@ -65,6 +65,7 @@ public final class MimeTypes {
public
static
final
String
APPLICATION_WEBM
=
BASE_TYPE_APPLICATION
+
"/webm"
;
public
static
final
String
APPLICATION_ID3
=
BASE_TYPE_APPLICATION
+
"/id3"
;
public
static
final
String
APPLICATION_CEA608
=
BASE_TYPE_APPLICATION
+
"/cea-608"
;
public
static
final
String
APPLICATION_CEA708
=
BASE_TYPE_APPLICATION
+
"/cea-708"
;
public
static
final
String
APPLICATION_SUBRIP
=
BASE_TYPE_APPLICATION
+
"/x-subrip"
;
public
static
final
String
APPLICATION_TTML
=
BASE_TYPE_APPLICATION
+
"/ttml+xml"
;
public
static
final
String
APPLICATION_M3U8
=
BASE_TYPE_APPLICATION
+
"/x-mpegURL"
;
...
...
playbacktests/src/main/AndroidManifest.xml
View file @
e2ff401e
...
...
@@ -17,8 +17,8 @@
<manifest
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:tools=
"http://schemas.android.com/tools"
package=
"com.google.android.exoplayer2.playbacktests"
android:versionCode=
"200
2
"
android:versionName=
"2.0.
2
"
>
android:versionCode=
"200
3
"
android:versionName=
"2.0.
3
"
>
<uses-permission
android:name=
"android.permission.INTERNET"
/>
<uses-permission
android:name=
"android.permission.WAKE_LOCK"
/>
...
...
testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeExtractorOutput.java
View file @
e2ff401e
...
...
@@ -23,7 +23,6 @@ import java.io.File;
import
java.io.IOException
;
import
java.io.PrintWriter
;
import
junit.framework.Assert
;
import
junit.framework.TestCase
;
/**
* A fake {@link ExtractorOutput}.
...
...
@@ -37,8 +36,6 @@ public final class FakeExtractorOutput implements ExtractorOutput, Dumper.Dumpab
*/
private
static
final
boolean
WRITE_DUMP
=
false
;
private
final
boolean
allowDuplicateTrackIds
;
public
final
SparseArray
<
FakeTrackOutput
>
trackOutputs
;
public
int
numberOfTracks
;
...
...
@@ -46,11 +43,6 @@ public final class FakeExtractorOutput implements ExtractorOutput, Dumper.Dumpab
public
SeekMap
seekMap
;
public
FakeExtractorOutput
()
{
this
(
false
);
}
public
FakeExtractorOutput
(
boolean
allowDuplicateTrackIds
)
{
this
.
allowDuplicateTrackIds
=
allowDuplicateTrackIds
;
trackOutputs
=
new
SparseArray
<>();
}
...
...
@@ -58,11 +50,10 @@ public final class FakeExtractorOutput implements ExtractorOutput, Dumper.Dumpab
public
FakeTrackOutput
track
(
int
trackId
)
{
FakeTrackOutput
output
=
trackOutputs
.
get
(
trackId
);
if
(
output
==
null
)
{
Assert
.
assertFalse
(
tracksEnded
);
numberOfTracks
++;
output
=
new
FakeTrackOutput
();
trackOutputs
.
put
(
trackId
,
output
);
}
else
{
TestCase
.
assertTrue
(
"Duplicate track id: "
+
trackId
,
allowDuplicateTrackIds
);
}
return
output
;
}
...
...
testutils/src/main/java/com/google/android/exoplayer2/testutil/TestUtil.java
View file @
e2ff401e
...
...
@@ -267,8 +267,8 @@ public class TestUtil {
*/
public
static
FakeExtractorOutput
assertOutput
(
Extractor
extractor
,
String
sampleFile
,
byte
[]
fileData
,
Instrumentation
instrumentation
,
boolean
simulateIOErrors
,
boolean
simulateUnknownLength
,
boolean
simulatePartialReads
)
throws
IOException
,
InterruptedException
{
boolean
simulateUnknownLength
,
boolean
simulatePartialReads
)
throws
IOException
,
InterruptedException
{
FakeExtractorInput
input
=
new
FakeExtractorInput
.
Builder
().
setData
(
fileData
)
.
setSimulateIOErrors
(
simulateIOErrors
)
.
setSimulateUnknownLength
(
simulateUnknownLength
)
...
...
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