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
e3a7fc4d
authored
May 19, 2015
by
ojw28
Browse files
Options
_('Browse Files')
Download
Plain Diff
Merge pull request #469 from google/dev
dev -> dev-webm-vp9-opus
parents
c4fd6c2b
763d68f2
Hide whitespace changes
Inline
Side-by-side
Showing
26 changed files
with
527 additions
and
181 deletions
README.md
RELEASENOTES.md
demo/src/main/AndroidManifest.xml
demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java
demo/src/main/java/com/google/android/exoplayer/demo/player/DashRendererBuilder.java
demo/src/main/java/com/google/android/exoplayer/demo/player/ExtractorRendererBuilder.java
demo/src/main/java/com/google/android/exoplayer/demo/player/HlsRendererBuilder.java
demo/src/main/java/com/google/android/exoplayer/demo/player/SmoothStreamingRendererBuilder.java
library/build.gradle
library/src/main/java/com/google/android/exoplayer/ExoPlayerLibraryInfo.java
library/src/main/java/com/google/android/exoplayer/audio/AudioTrack.java
library/src/main/java/com/google/android/exoplayer/chunk/VideoFormatSelectorUtil.java
library/src/main/java/com/google/android/exoplayer/drm/StreamingDrmSessionManager.java
library/src/main/java/com/google/android/exoplayer/extractor/mp3/Mp3Extractor.java
library/src/main/java/com/google/android/exoplayer/extractor/mp3/VbriSeeker.java
library/src/main/java/com/google/android/exoplayer/extractor/mp3/XingSeeker.java
library/src/main/java/com/google/android/exoplayer/extractor/ts/TsExtractor.java
library/src/main/java/com/google/android/exoplayer/hls/HlsParserUtil.java
library/src/main/java/com/google/android/exoplayer/hls/HlsPlaylistParser.java
library/src/main/java/com/google/android/exoplayer/upstream/AssetDataSource.java
library/src/main/java/com/google/android/exoplayer/upstream/ContentDataSource.java
library/src/main/java/com/google/android/exoplayer/upstream/DefaultBandwidthMeter.java
library/src/main/java/com/google/android/exoplayer/upstream/DefaultUriDataSource.java
library/src/main/java/com/google/android/exoplayer/upstream/FileDataSource.java
library/src/main/java/com/google/android/exoplayer/upstream/HttpDataSource.java
library/src/main/java/com/google/android/exoplayer/util/ParsableBitArray.java
README.md
View file @
e3a7fc4d
...
...
@@ -16,18 +16,16 @@ Read news, hints and tips on the [news][] page.
[
news
]:
https://google.github.io/ExoPlayer/news.html
## D
eveloper guide
##
## D
ocumentation
##
The
[
developer guide
][]
provides a wealth of information to help you get
*
The
[
developer guide
][]
provides a wealth of information to help you get
started.
*
The
[
class reference
][]
documents the ExoPlayer library classes.
*
The
[
release notes
][]
document the major changes in each release.
[
developer guide
]:
https://google.github.io/ExoPlayer/guide.html
## Reference documentation ##
The
[
class reference
][]
documents the ExoPlayer library classes.
[
class reference
]:
https://google.github.io/ExoPlayer/doc/reference
[
release notes
]:
https://github.com/google/ExoPlayer/blob/dev/RELEASENOTES.md
## Project branches ##
...
...
RELEASENOTES.md
0 → 100644
View file @
e3a7fc4d
# Release notes #
### r1.3.2 ###
*
DataSource improvements:
`DefaultUriDataSource`
now handles http://, https://, file://, asset://
and content:// URIs automatically. It also handles file:///android_asset/
*
URIs, and file paths
like /path/to/media.mp4 where the scheme is omitted.
*
HLS: Fix for some ID3 events being dropped.
*
HLS: Correctly handle 0x0 and floating point RESOLUTION tags.
*
Mp3Extractor: robustness improvements.
### r1.3.1 ###
*
No notes provided.
demo/src/main/AndroidManifest.xml
View file @
e3a7fc4d
...
...
@@ -16,8 +16,8 @@
<manifest
xmlns:android=
"http://schemas.android.com/apk/res/android"
package=
"com.google.android.exoplayer.demo"
android:versionCode=
"130
1
"
android:versionName=
"1.3.
1
"
android:versionCode=
"130
2
"
android:versionName=
"1.3.
2
"
android:theme=
"@style/RootTheme"
>
<uses-permission
android:name=
"android.permission.INTERNET"
/>
...
...
demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java
View file @
e3a7fc4d
...
...
@@ -233,19 +233,19 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
audioCapabilities
);
case
DemoUtil
.
TYPE_M4A
:
// There are no file format differences between M4A and MP4.
case
DemoUtil
.
TYPE_MP4
:
return
new
ExtractorRendererBuilder
(
userAgent
,
contentUri
,
debugTextView
,
return
new
ExtractorRendererBuilder
(
this
,
userAgent
,
contentUri
,
debugTextView
,
new
Mp4Extractor
());
case
DemoUtil
.
TYPE_MP3
:
return
new
ExtractorRendererBuilder
(
userAgent
,
contentUri
,
debugTextView
,
return
new
ExtractorRendererBuilder
(
this
,
userAgent
,
contentUri
,
debugTextView
,
new
Mp3Extractor
());
case
DemoUtil
.
TYPE_TS
:
return
new
ExtractorRendererBuilder
(
userAgent
,
contentUri
,
debugTextView
,
return
new
ExtractorRendererBuilder
(
this
,
userAgent
,
contentUri
,
debugTextView
,
new
TsExtractor
(
0
,
audioCapabilities
));
case
DemoUtil
.
TYPE_AAC
:
return
new
ExtractorRendererBuilder
(
userAgent
,
contentUri
,
debugTextView
,
return
new
ExtractorRendererBuilder
(
this
,
userAgent
,
contentUri
,
debugTextView
,
new
AdtsExtractor
());
case
DemoUtil
.
TYPE_WEBM
:
return
new
ExtractorRendererBuilder
(
userAgent
,
contentUri
,
debugTextView
,
return
new
ExtractorRendererBuilder
(
this
,
userAgent
,
contentUri
,
debugTextView
,
new
WebmExtractor
());
default
:
throw
new
IllegalStateException
(
"Unsupported type: "
+
contentType
);
...
...
demo/src/main/java/com/google/android/exoplayer/demo/player/DashRendererBuilder.java
View file @
e3a7fc4d
...
...
@@ -130,7 +130,7 @@ public class DashRendererBuilder implements RendererBuilder,
this
.
player
=
player
;
this
.
callback
=
callback
;
MediaPresentationDescriptionParser
parser
=
new
MediaPresentationDescriptionParser
();
manifestDataSource
=
new
DefaultUriDataSource
(
userAgent
,
null
);
manifestDataSource
=
new
DefaultUriDataSource
(
context
,
userAgent
);
manifestFetcher
=
new
ManifestFetcher
<
MediaPresentationDescription
>(
url
,
manifestDataSource
,
parser
);
manifestFetcher
.
singleLoad
(
player
.
getMainHandler
().
getLooper
(),
this
);
...
...
@@ -232,10 +232,10 @@ public class DashRendererBuilder implements RendererBuilder,
videoRenderer
=
null
;
debugRenderer
=
null
;
}
else
{
DataSource
videoDataSource
=
new
DefaultUriDataSource
(
userAgent
,
bandwidthMeter
);
ChunkSource
videoChunkSource
=
new
DashChunkSource
(
manifestFetcher
,
videoAdaptationSetIndex
,
video
RepresentationIndices
,
videoDataSource
,
new
AdaptiveEvaluator
(
bandwidthMeter
)
,
LIVE_EDGE_LATENCY_MS
,
elapsedRealtimeOffset
);
DataSource
videoDataSource
=
new
DefaultUriDataSource
(
context
,
bandwidthMeter
,
userAgent
);
ChunkSource
videoChunkSource
=
new
DashChunkSource
(
manifestFetcher
,
video
AdaptationSetIndex
,
videoRepresentationIndices
,
videoDataSource
,
new
AdaptiveEvaluator
(
bandwidthMeter
),
LIVE_EDGE_LATENCY_MS
,
elapsedRealtimeOffset
);
ChunkSampleSource
videoSampleSource
=
new
ChunkSampleSource
(
videoChunkSource
,
loadControl
,
VIDEO_BUFFER_SEGMENTS
*
BUFFER_SEGMENT_SIZE
,
true
,
mainHandler
,
player
,
DemoPlayer
.
TYPE_VIDEO
);
...
...
@@ -249,7 +249,7 @@ public class DashRendererBuilder implements RendererBuilder,
List
<
ChunkSource
>
audioChunkSourceList
=
new
ArrayList
<
ChunkSource
>();
List
<
String
>
audioTrackNameList
=
new
ArrayList
<
String
>();
if
(
audioAdaptationSet
!=
null
)
{
DataSource
audioDataSource
=
new
DefaultUriDataSource
(
userAgent
,
bandwidthMeter
);
DataSource
audioDataSource
=
new
DefaultUriDataSource
(
context
,
bandwidthMeter
,
userAgent
);
FormatEvaluator
audioEvaluator
=
new
FormatEvaluator
.
FixedEvaluator
();
List
<
Representation
>
audioRepresentations
=
audioAdaptationSet
.
representations
;
List
<
String
>
codecs
=
new
ArrayList
<
String
>();
...
...
@@ -304,7 +304,7 @@ public class DashRendererBuilder implements RendererBuilder,
}
// Build the text chunk sources.
DataSource
textDataSource
=
new
DefaultUriDataSource
(
userAgent
,
bandwidthMeter
);
DataSource
textDataSource
=
new
DefaultUriDataSource
(
context
,
bandwidthMeter
,
userAgent
);
FormatEvaluator
textEvaluator
=
new
FormatEvaluator
.
FixedEvaluator
();
List
<
ChunkSource
>
textChunkSourceList
=
new
ArrayList
<
ChunkSource
>();
List
<
String
>
textTrackNameList
=
new
ArrayList
<
String
>();
...
...
demo/src/main/java/com/google/android/exoplayer/demo/player/ExtractorRendererBuilder.java
View file @
e3a7fc4d
...
...
@@ -25,6 +25,7 @@ import com.google.android.exoplayer.extractor.ExtractorSampleSource;
import
com.google.android.exoplayer.upstream.DataSource
;
import
com.google.android.exoplayer.upstream.DefaultUriDataSource
;
import
android.content.Context
;
import
android.media.MediaCodec
;
import
android.net.Uri
;
import
android.widget.TextView
;
...
...
@@ -36,13 +37,15 @@ public class ExtractorRendererBuilder implements RendererBuilder {
private
static
final
int
BUFFER_SIZE
=
10
*
1024
*
1024
;
private
final
Context
context
;
private
final
String
userAgent
;
private
final
Uri
uri
;
private
final
TextView
debugTextView
;
private
final
Extractor
extractor
;
public
ExtractorRendererBuilder
(
String
userAgent
,
Uri
uri
,
TextView
debugTextView
,
Extractor
extractor
)
{
public
ExtractorRendererBuilder
(
Context
context
,
String
userAgent
,
Uri
uri
,
TextView
debugTextView
,
Extractor
extractor
)
{
this
.
context
=
context
;
this
.
userAgent
=
userAgent
;
this
.
uri
=
uri
;
this
.
debugTextView
=
debugTextView
;
...
...
@@ -52,7 +55,7 @@ public class ExtractorRendererBuilder implements RendererBuilder {
@Override
public
void
buildRenderers
(
DemoPlayer
player
,
RendererBuilderCallback
callback
)
{
// Build the video and audio renderers.
DataSource
dataSource
=
new
DefaultUriDataSource
(
userAgent
,
null
);
DataSource
dataSource
=
new
DefaultUriDataSource
(
context
,
userAgent
);
ExtractorSampleSource
sampleSource
=
new
ExtractorSampleSource
(
uri
,
dataSource
,
extractor
,
2
,
BUFFER_SIZE
);
MediaCodecVideoTrackRenderer
videoRenderer
=
new
MediaCodecVideoTrackRenderer
(
sampleSource
,
...
...
demo/src/main/java/com/google/android/exoplayer/demo/player/HlsRendererBuilder.java
View file @
e3a7fc4d
...
...
@@ -76,8 +76,8 @@ public class HlsRendererBuilder implements RendererBuilder, ManifestCallback<Hls
this
.
player
=
player
;
this
.
callback
=
callback
;
HlsPlaylistParser
parser
=
new
HlsPlaylistParser
();
ManifestFetcher
<
HlsPlaylist
>
playlistFetcher
=
new
ManifestFetcher
<
HlsPlaylist
>(
url
,
new
DefaultUriDataSource
(
userAgent
,
null
),
parser
);
ManifestFetcher
<
HlsPlaylist
>
playlistFetcher
=
new
ManifestFetcher
<
HlsPlaylist
>(
url
,
new
DefaultUriDataSource
(
context
,
userAgent
),
parser
);
playlistFetcher
.
singleLoad
(
player
.
getMainHandler
().
getLooper
(),
this
);
}
...
...
@@ -103,7 +103,7 @@ public class HlsRendererBuilder implements RendererBuilder, ManifestCallback<Hls
}
}
DataSource
dataSource
=
new
DefaultUriDataSource
(
userAgent
,
bandwidthMeter
);
DataSource
dataSource
=
new
DefaultUriDataSource
(
context
,
bandwidthMeter
,
userAgent
);
HlsChunkSource
chunkSource
=
new
HlsChunkSource
(
dataSource
,
url
,
manifest
,
bandwidthMeter
,
variantIndices
,
HlsChunkSource
.
ADAPTIVE_MODE_SPLICE
,
audioCapabilities
);
HlsSampleSource
sampleSource
=
new
HlsSampleSource
(
chunkSource
,
true
,
3
,
REQUESTED_BUFFER_SIZE
,
...
...
demo/src/main/java/com/google/android/exoplayer/demo/player/SmoothStreamingRendererBuilder.java
View file @
e3a7fc4d
...
...
@@ -92,8 +92,12 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
public
void
buildRenderers
(
DemoPlayer
player
,
RendererBuilderCallback
callback
)
{
this
.
player
=
player
;
this
.
callback
=
callback
;
String
manifestUrl
=
url
;
if
(!
manifestUrl
.
endsWith
(
"/Manifest"
))
{
manifestUrl
+=
"/Manifest"
;
}
SmoothStreamingManifestParser
parser
=
new
SmoothStreamingManifestParser
();
manifestFetcher
=
new
ManifestFetcher
<
SmoothStreamingManifest
>(
url
+
"/Manifest"
,
manifestFetcher
=
new
ManifestFetcher
<
SmoothStreamingManifest
>(
manifestUrl
,
new
DefaultHttpDataSource
(
userAgent
,
null
),
parser
);
manifestFetcher
.
singleLoad
(
player
.
getMainHandler
().
getLooper
(),
this
);
}
...
...
@@ -160,7 +164,7 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
videoRenderer
=
null
;
debugRenderer
=
null
;
}
else
{
DataSource
videoDataSource
=
new
DefaultUriDataSource
(
userAgent
,
bandwidthMeter
);
DataSource
videoDataSource
=
new
DefaultUriDataSource
(
context
,
bandwidthMeter
,
userAgent
);
ChunkSource
videoChunkSource
=
new
SmoothStreamingChunkSource
(
manifestFetcher
,
videoStreamElementIndex
,
videoTrackIndices
,
videoDataSource
,
new
AdaptiveEvaluator
(
bandwidthMeter
),
LIVE_EDGE_LATENCY_MS
);
...
...
@@ -184,7 +188,7 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
}
else
{
audioTrackNames
=
new
String
[
audioStreamElementCount
];
ChunkSource
[]
audioChunkSources
=
new
ChunkSource
[
audioStreamElementCount
];
DataSource
audioDataSource
=
new
DefaultUriDataSource
(
userAgent
,
bandwidthMeter
);
DataSource
audioDataSource
=
new
DefaultUriDataSource
(
context
,
bandwidthMeter
,
userAgent
);
FormatEvaluator
audioFormatEvaluator
=
new
FormatEvaluator
.
FixedEvaluator
();
audioStreamElementCount
=
0
;
for
(
int
i
=
0
;
i
<
manifest
.
streamElements
.
length
;
i
++)
{
...
...
@@ -215,7 +219,7 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
}
else
{
textTrackNames
=
new
String
[
textStreamElementCount
];
ChunkSource
[]
textChunkSources
=
new
ChunkSource
[
textStreamElementCount
];
DataSource
ttmlDataSource
=
new
DefaultUriDataSource
(
userAgent
,
bandwidthMeter
);
DataSource
ttmlDataSource
=
new
DefaultUriDataSource
(
context
,
bandwidthMeter
,
userAgent
);
FormatEvaluator
ttmlFormatEvaluator
=
new
FormatEvaluator
.
FixedEvaluator
();
textStreamElementCount
=
0
;
for
(
int
i
=
0
;
i
<
manifest
.
streamElements
.
length
;
i
++)
{
...
...
library/build.gradle
View file @
e3a7fc4d
...
...
@@ -74,7 +74,7 @@ publish {
userOrg
=
'google'
groupId
=
'com.google.android.exoplayer'
artifactId
=
'exoplayer'
version
=
'r1.3.
1
'
version
=
'r1.3.
2
'
description
=
'The ExoPlayer library.'
website
=
'https://github.com/google/ExoPlayer'
}
library/src/main/java/com/google/android/exoplayer/ExoPlayerLibraryInfo.java
View file @
e3a7fc4d
...
...
@@ -26,7 +26,7 @@ public class ExoPlayerLibraryInfo {
/**
* The version of the library, expressed as a string.
*/
public
static
final
String
VERSION
=
"1.3.
1
"
;
public
static
final
String
VERSION
=
"1.3.
2
"
;
/**
* The version of the library, expressed as an integer.
...
...
@@ -34,7 +34,7 @@ public class ExoPlayerLibraryInfo {
* Three digits are used for each component of {@link #VERSION}. For example "1.2.3" has the
* corresponding integer version 001002003.
*/
public
static
final
int
VERSION_INT
=
00100300
1
;
public
static
final
int
VERSION_INT
=
00100300
2
;
/**
* Whether the library was compiled with {@link com.google.android.exoplayer.util.Assertions}
...
...
library/src/main/java/com/google/android/exoplayer/audio/AudioTrack.java
View file @
e3a7fc4d
...
...
@@ -574,6 +574,7 @@ public final class AudioTrack {
submittedBytes
=
0
;
temporaryBufferSize
=
0
;
startMediaTimeUs
=
START_NOT_SET
;
latencyUs
=
0
;
resetSyncParams
();
int
playState
=
audioTrack
.
getPlayState
();
if
(
playState
==
android
.
media
.
AudioTrack
.
PLAYSTATE_PLAYING
)
{
...
...
@@ -647,9 +648,10 @@ public final class AudioTrack {
}
}
if
(
systemClockUs
-
lastTimestampSampleTimeUs
>=
MIN_TIMESTAMP_SAMPLE_INTERVAL_US
)
{
// Don't use AudioTrack.getTimestamp() on AC-3 tracks, as it gives an incorrect timestamp.
audioTimestampSet
=
!
isAc3
&&
audioTrackUtil
.
updateTimestamp
();
// Don't sample the timestamp and latency if this is an AC-3 passthrough AudioTrack, as the
// returned values cause audio/video synchronization to be incorrect.
if
(!
isAc3
&&
systemClockUs
-
lastTimestampSampleTimeUs
>=
MIN_TIMESTAMP_SAMPLE_INTERVAL_US
)
{
audioTimestampSet
=
audioTrackUtil
.
updateTimestamp
();
if
(
audioTimestampSet
)
{
// Perform sanity checks on the timestamp.
long
audioTimestampUs
=
audioTrackUtil
.
getTimestampNanoTime
()
/
1000
;
...
...
library/src/main/java/com/google/android/exoplayer/chunk/VideoFormatSelectorUtil.java
View file @
e3a7fc4d
...
...
@@ -108,7 +108,7 @@ public final class VideoFormatSelectorUtil {
// Keep track of the number of pixels of the selected format whose resolution is the
// smallest to exceed the maximum size at which it can be displayed within the viewport.
// We'll discard formats of higher resolution in a second pass.
if
(
format
.
width
!=
-
1
&&
format
.
height
!=
-
1
)
{
if
(
format
.
width
>
0
&&
format
.
height
>
0
)
{
Point
maxVideoSizeInViewport
=
getMaxVideoSizeInViewport
(
orientationMayChange
,
viewportWidth
,
viewportHeight
,
format
.
width
,
format
.
height
);
int
videoPixels
=
format
.
width
*
format
.
height
;
...
...
@@ -126,7 +126,7 @@ public final class VideoFormatSelectorUtil {
// viewport.
for
(
int
i
=
selectedIndexList
.
size
()
-
1
;
i
>=
0
;
i
--)
{
Format
format
=
formatWrappers
.
get
(
i
).
getFormat
();
if
(
format
.
width
!=
-
1
&&
format
.
height
!=
-
1
if
(
format
.
width
>
0
&&
format
.
height
>
0
&&
format
.
width
*
format
.
height
>
maxVideoPixelsToRetain
)
{
selectedIndexList
.
remove
(
i
);
}
...
...
@@ -150,7 +150,7 @@ public final class VideoFormatSelectorUtil {
// Filtering format because it's HD.
return
false
;
}
if
(
format
.
width
!=
-
1
&&
format
.
height
!=
-
1
)
{
if
(
format
.
width
>
0
&&
format
.
height
>
0
)
{
// TODO: Use MediaCodecUtil.isSizeAndRateSupportedV21 on API levels >= 21 if we know the
// mimeType of the media samples within the container. Remove the assumption that we're
// dealing with H.264.
...
...
library/src/main/java/com/google/android/exoplayer/drm/StreamingDrmSessionManager.java
View file @
e3a7fc4d
...
...
@@ -214,6 +214,18 @@ public class StreamingDrmSessionManager implements DrmSessionManager {
}
/**
* Provides access to {@link MediaDrm#setPropertyString(String, String)}.
* <p>
* This method may be called when the manager is in any state.
*
* @param key The property to write.
* @param value The value to write.
*/
public
final
void
setPropertyString
(
String
key
,
String
value
)
{
mediaDrm
.
setPropertyString
(
key
,
value
);
}
/**
* Provides access to {@link MediaDrm#getPropertyByteArray(String)}.
* <p>
* This method may be called when the manager is in any state.
...
...
@@ -225,6 +237,18 @@ public class StreamingDrmSessionManager implements DrmSessionManager {
return
mediaDrm
.
getPropertyByteArray
(
key
);
}
/**
* Provides access to {@link MediaDrm#setPropertyByteArray(String, byte[])}.
* <p>
* This method may be called when the manager is in any state.
*
* @param key The property to write.
* @param value The value to write.
*/
public
final
void
setPropertyByteArray
(
String
key
,
byte
[]
value
)
{
mediaDrm
.
setPropertyByteArray
(
key
,
value
);
}
@Override
public
void
open
(
DrmInitData
drmInitData
)
{
if
(++
openCount
!=
1
)
{
...
...
library/src/main/java/com/google/android/exoplayer/extractor/mp3/Mp3Extractor.java
View file @
e3a7fc4d
...
...
@@ -45,6 +45,9 @@ public final class Mp3Extractor implements Extractor {
private
static
final
int
ID3_TAG
=
Util
.
getIntegerCodeForString
(
"ID3"
);
private
static
final
String
[]
MIME_TYPE_BY_LAYER
=
new
String
[]
{
MimeTypes
.
AUDIO_MPEG_L1
,
MimeTypes
.
AUDIO_MPEG_L2
,
MimeTypes
.
AUDIO_MPEG
};
private
static
final
int
XING_HEADER
=
Util
.
getIntegerCodeForString
(
"Xing"
);
private
static
final
int
INFO_HEADER
=
Util
.
getIntegerCodeForString
(
"Info"
);
private
static
final
int
VBRI_HEADER
=
Util
.
getIntegerCodeForString
(
"VBRI"
);
/**
* Theoretical maximum frame size for an MPEG audio stream, which occurs when playing a Layer 2
...
...
@@ -172,6 +175,15 @@ public final class Mp3Extractor implements Extractor {
}
private
long
synchronize
(
ExtractorInput
extractorInput
)
throws
IOException
,
InterruptedException
{
if
(
extractorInput
.
getPosition
()
==
0
)
{
// Before preparation completes, retrying loads from the start, so clear any buffered data.
inputBuffer
.
reset
();
}
else
{
// After preparation completes, retrying resumes loading from the old position, so return to
// the start of buffered data to parse it again.
inputBuffer
.
returnToMark
();
}
long
startPosition
=
getPosition
(
extractorInput
,
inputBuffer
);
// Skip any ID3 header at the start of the file.
...
...
@@ -195,6 +207,7 @@ public final class Mp3Extractor implements Extractor {
inputBuffer
.
mark
();
long
headerPosition
=
startPosition
;
int
validFrameCount
=
0
;
int
candidateSynchronizedHeaderData
=
0
;
while
(
true
)
{
if
(
headerPosition
-
startPosition
>=
MAX_BYTES_TO_SEARCH
)
{
throw
new
ParserException
(
"Searched too many bytes while resynchronizing."
);
...
...
@@ -207,11 +220,11 @@ public final class Mp3Extractor implements Extractor {
scratch
.
setPosition
(
0
);
int
headerData
=
scratch
.
readInt
();
int
frameSize
;
if
((
s
ynchronizedHeaderData
!=
0
&&
(
headerData
&
HEADER_MASK
)
!=
(
s
ynchronizedHeaderData
&
HEADER_MASK
))
if
((
candidateS
ynchronizedHeaderData
!=
0
&&
(
headerData
&
HEADER_MASK
)
!=
(
candidateS
ynchronizedHeaderData
&
HEADER_MASK
))
||
(
frameSize
=
MpegAudioHeader
.
getFrameSize
(
headerData
))
==
-
1
)
{
validFrameCount
=
0
;
s
ynchronizedHeaderData
=
0
;
candidateS
ynchronizedHeaderData
=
0
;
// Try reading a header starting at the next byte.
inputBuffer
.
returnToMark
();
...
...
@@ -223,7 +236,7 @@ public final class Mp3Extractor implements Extractor {
if
(
validFrameCount
==
0
)
{
MpegAudioHeader
.
populateHeader
(
headerData
,
synchronizedHeader
);
s
ynchronizedHeaderData
=
headerData
;
candidateS
ynchronizedHeaderData
=
headerData
;
}
// The header was valid and matching (if appropriate). Check another or end synchronization.
...
...
@@ -238,22 +251,9 @@ public final class Mp3Extractor implements Extractor {
// The input buffer read position is now synchronized.
inputBuffer
.
returnToMark
();
synchronizedHeaderData
=
candidateSynchronizedHeaderData
;
if
(
seeker
==
null
)
{
ParsableByteArray
frame
=
inputBuffer
.
getParsableByteArray
(
extractorInput
,
synchronizedHeader
.
frameSize
);
seeker
=
XingSeeker
.
create
(
synchronizedHeader
,
frame
,
headerPosition
,
extractorInput
.
getLength
());
if
(
seeker
==
null
)
{
seeker
=
VbriSeeker
.
create
(
synchronizedHeader
,
frame
,
headerPosition
);
}
if
(
seeker
==
null
)
{
inputBuffer
.
returnToMark
();
seeker
=
new
ConstantBitrateSeeker
(
headerPosition
,
synchronizedHeader
.
bitrate
*
1000
,
extractorInput
.
getLength
());
}
else
{
// Discard the frame that was parsed for seeking metadata.
inputBuffer
.
mark
();
}
setupSeeker
(
extractorInput
,
headerPosition
);
extractorOutput
.
seekMap
(
seeker
);
trackOutput
.
format
(
MediaFormat
.
createAudioFormat
(
MIME_TYPE_BY_LAYER
[
synchronizedHeader
.
layerIndex
],
MAX_FRAME_SIZE_BYTES
,
...
...
@@ -264,6 +264,93 @@ public final class Mp3Extractor implements Extractor {
return
headerPosition
;
}
/**
* Sets {@link #seeker} to seek using metadata from {@link #inputBuffer}, which should have its
* position set to the start of the first frame in the stream. On returning,
* {@link #inputBuffer}'s position and mark will be set to the start of the first frame of audio.
*
* @param extractorInput Source of data for {@link #inputBuffer}.
* @param headerPosition Position (byte offset) of the synchronized header in the stream.
* @throws IOException Thrown if there was an error reading from the stream. Not expected if the
* next two frames were already read during synchronization.
* @throws InterruptedException Thrown if reading from the stream was interrupted. Not expected if
* the next two frames were already read during synchronization.
*/
private
void
setupSeeker
(
ExtractorInput
extractorInput
,
long
headerPosition
)
throws
IOException
,
InterruptedException
{
// Try to set up seeking based on a XING or VBRI header.
if
(
parseSeekerFrame
(
extractorInput
,
headerPosition
,
extractorInput
.
getLength
()))
{
// Discard the parsed header so we start reading from the first audio frame.
inputBuffer
.
mark
();
if
(
seeker
!=
null
)
{
return
;
}
// If there was a header but it was not usable, synchronize to the next frame so we don't
// use an invalid bitrate for CBR seeking. This read is guaranteed to succeed if the frame was
// already read during synchronization.
inputBuffer
.
read
(
extractorInput
,
scratch
.
data
,
0
,
4
);
scratch
.
setPosition
(
0
);
headerPosition
+=
synchronizedHeader
.
frameSize
;
MpegAudioHeader
.
populateHeader
(
scratch
.
readInt
(),
synchronizedHeader
);
}
inputBuffer
.
returnToMark
();
seeker
=
new
ConstantBitrateSeeker
(
headerPosition
,
synchronizedHeader
.
bitrate
*
1000
,
extractorInput
.
getLength
());
}
/**
* Consumes the frame at {@link #inputBuffer}'s current position, advancing it to the next frame.
* The mark is not modified. {@link #seeker} will be assigned based on seeking metadata in the
* frame. If there is no seeking metadata, returns {@code false} and sets {@link #seeker} to null.
* If seeking metadata is present and unusable, returns {@code true} and sets {@link #seeker} to
* null. Otherwise, returns {@code true} and assigns {@link #seeker}.
*/
private
boolean
parseSeekerFrame
(
ExtractorInput
extractorInput
,
long
headerPosition
,
long
inputLength
)
throws
IOException
,
InterruptedException
{
// Read the first frame so it can be parsed for seeking metadata.
inputBuffer
.
mark
();
seeker
=
null
;
ParsableByteArray
frame
=
inputBuffer
.
getParsableByteArray
(
extractorInput
,
synchronizedHeader
.
frameSize
);
// Check if there is a XING header.
int
xingBase
;
if
((
synchronizedHeader
.
version
&
1
)
==
1
)
{
// MPEG 1.
if
(
synchronizedHeader
.
channels
!=
1
)
{
xingBase
=
32
;
}
else
{
xingBase
=
17
;
}
}
else
{
// MPEG 2 or 2.5.
if
(
synchronizedHeader
.
channels
!=
1
)
{
xingBase
=
17
;
}
else
{
xingBase
=
9
;
}
}
frame
.
setPosition
(
4
+
xingBase
);
int
headerData
=
frame
.
readInt
();
if
(
headerData
==
XING_HEADER
||
headerData
==
INFO_HEADER
)
{
seeker
=
XingSeeker
.
create
(
synchronizedHeader
,
frame
,
headerPosition
,
inputLength
);
return
true
;
}
// Check if there is a VBRI header.
frame
.
setPosition
(
36
);
// MPEG audio header (4 bytes) + 32 bytes.
headerData
=
frame
.
readInt
();
if
(
headerData
==
VBRI_HEADER
)
{
seeker
=
VbriSeeker
.
create
(
synchronizedHeader
,
frame
,
headerPosition
);
return
true
;
}
// Neither header is present.
return
false
;
}
/** Returns the reading position of {@code bufferingInput} relative to the extractor's stream. */
private
static
long
getPosition
(
ExtractorInput
extractorInput
,
BufferingInput
bufferingInput
)
{
return
extractorInput
.
getPosition
()
-
bufferingInput
.
getAvailableByteCount
();
...
...
library/src/main/java/com/google/android/exoplayer/extractor/mp3/VbriSeeker.java
View file @
e3a7fc4d
...
...
@@ -23,23 +23,20 @@ import com.google.android.exoplayer.util.Util;
*/
/* package */
final
class
VbriSeeker
implements
Mp3Extractor
.
Seeker
{
private
static
final
int
VBRI_HEADER
=
Util
.
getIntegerCodeForString
(
"VBRI"
);
/**
* If {@code frame} contains a VBRI header and it is usable for seeking, returns a
* {@link VbriSeeker} for seeking in the containing stream. Otherwise, returns {@code null}, which
* indicates that the information in the frame was not a VBRI header, or was unusable for seeking.
* Returns a {@link VbriSeeker} for seeking in the stream, if required information is present.
* Returns {@code null} if not. On returning, {@code frame}'s position is not specified so the
* caller should reset it.
*
* @param mpegAudioHeader The MPEG audio header associated with the frame.
* @param frame The data in this audio frame, with its position set to immediately after the
* 'VBRI' tag.
* @param position The position (byte offset) of the start of this frame in the stream.
* @return A {@link VbriSeeker} for seeking in the stream, or {@code null} if the required
* information is not present.
*/
public
static
VbriSeeker
create
(
MpegAudioHeader
mpegAudioHeader
,
ParsableByteArray
frame
,
long
position
)
{
long
basePosition
=
position
+
mpegAudioHeader
.
frameSize
;
// Read the VBRI header.
frame
.
skipBytes
(
32
);
int
headerData
=
frame
.
readInt
();
if
(
headerData
!=
VBRI_HEADER
)
{
return
null
;
}
public
static
VbriSeeker
create
(
MpegAudioHeader
mpegAudioHeader
,
ParsableByteArray
frame
,
long
position
)
{
frame
.
skipBytes
(
10
);
int
numFrames
=
frame
.
readInt
();
if
(
numFrames
<=
0
)
{
...
...
@@ -83,7 +80,7 @@ import com.google.android.exoplayer.util.Util;
segmentIndex
++;
}
return
new
VbriSeeker
(
timesUs
,
offsets
,
basePosition
,
durationUs
);
return
new
VbriSeeker
(
timesUs
,
offsets
,
position
+
mpegAudioHeader
.
frameSize
,
durationUs
);
}
private
final
long
[]
timesUs
;
...
...
library/src/main/java/com/google/android/exoplayer/extractor/mp3/XingSeeker.java
View file @
e3a7fc4d
...
...
@@ -24,13 +24,18 @@ import com.google.android.exoplayer.util.Util;
*/
/* package */
final
class
XingSeeker
implements
Mp3Extractor
.
Seeker
{
private
static
final
int
XING_HEADER
=
Util
.
getIntegerCodeForString
(
"Xing"
);
private
static
final
int
INFO_HEADER
=
Util
.
getIntegerCodeForString
(
"Info"
);
/**
* If {@code frame} contains a XING header and it is usable for seeking, returns a
* {@link XingSeeker} for seeking in the containing stream. Otherwise, returns {@code null}, which
* indicates that the information in the frame was not a XING header, or was unusable for seeking.
* Returns a {@link XingSeeker} for seeking in the stream, if required information is present.
* Returns {@code null} if not. On returning, {@code frame}'s position is not specified so the
* caller should reset it.
*
* @param mpegAudioHeader The MPEG audio header associated with the frame.
* @param frame The data in this audio frame, with its position set to immediately after the
* 'XING' or 'INFO' tag.
* @param position The position (byte offset) of the start of this frame in the stream.
* @param inputLength The length of the stream in bytes.
* @return A {@link XingSeeker} for seeking in the stream, or {@code null} if the required
* information is not present.
*/
public
static
XingSeeker
create
(
MpegAudioHeader
mpegAudioHeader
,
ParsableByteArray
frame
,
long
position
,
long
inputLength
)
{
...
...
@@ -38,29 +43,6 @@ import com.google.android.exoplayer.util.Util;
int
sampleRate
=
mpegAudioHeader
.
sampleRate
;
long
firstFramePosition
=
position
+
mpegAudioHeader
.
frameSize
;
// Skip to the XING header.
int
xingBase
;
if
((
mpegAudioHeader
.
version
&
1
)
==
1
)
{
// MPEG 1.
if
(
mpegAudioHeader
.
channels
!=
1
)
{
xingBase
=
32
;
}
else
{
xingBase
=
17
;
}
}
else
{
// MPEG 2 or 2.5.
if
(
mpegAudioHeader
.
channels
!=
1
)
{
xingBase
=
17
;
}
else
{
xingBase
=
9
;
}
}
frame
.
skipBytes
(
4
+
xingBase
);
int
headerData
=
frame
.
readInt
();
if
(
headerData
!=
XING_HEADER
&&
headerData
!=
INFO_HEADER
)
{
return
null
;
}
int
flags
=
frame
.
readInt
();
// Frame count, size and table of contents are required to use this header.
if
((
flags
&
0x07
)
!=
0x07
)
{
...
...
library/src/main/java/com/google/android/exoplayer/extractor/ts/TsExtractor.java
View file @
e3a7fc4d
...
...
@@ -52,16 +52,17 @@ public final class TsExtractor implements Extractor, SeekMap {
private
static
final
long
MAX_PTS
=
0x1FFFFFFFF
L
;
private
final
ParsableByteArray
tsPacketBuffer
;
private
final
SparseBooleanArray
streamTypes
;
private
final
SparseBooleanArray
allowedPassthroughStreamTypes
;
private
final
SparseArray
<
TsPayloadReader
>
tsPayloadReaders
;
// Indexed by pid
private
final
long
firstSampleTimestampUs
;
private
final
ParsableBitArray
tsScratch
;
private
final
long
firstSampleTimestampUs
;
/* package */
final
SparseBooleanArray
streamTypes
;
/* package */
final
SparseBooleanArray
allowedPassthroughStreamTypes
;
/* package */
final
SparseArray
<
TsPayloadReader
>
tsPayloadReaders
;
// Indexed by pid
// Accessed only by the loading thread.
private
ExtractorOutput
output
;
private
long
timestampOffsetUs
;
private
long
lastPts
;
/* package */
Id3Reader
id3Reader
;
public
TsExtractor
()
{
this
(
0
,
null
);
...
...
@@ -307,9 +308,11 @@ public final class TsExtractor implements Extractor, SeekMap {
// Skip the descriptors.
data
.
skipBytes
(
programInfoLength
);
// Setup an ID3 track regardless of whether there's a corresponding entry, in case one
// appears intermittently during playback. See b/20261500.
Id3Reader
id3Reader
=
new
Id3Reader
(
output
.
track
(
TS_STREAM_TYPE_ID3
));
if
(
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
(
TS_STREAM_TYPE_ID3
));
}
int
entriesSize
=
sectionLength
-
9
/* Size of the rest of the fields before descriptors */
-
programInfoLength
-
4
/* CRC size */
;
...
...
@@ -532,11 +535,11 @@ public final class TsExtractor implements Extractor, SeekMap {
timeUs
=
0
;
if
(
ptsFlag
)
{
pesScratch
.
skipBits
(
4
);
// '0010'
long
pts
=
pesScratch
.
readBitsLong
(
3
)
<<
30
;
long
pts
=
(
long
)
pesScratch
.
readBits
(
3
)
<<
30
;
pesScratch
.
skipBits
(
1
);
// marker_bit
pts
|=
pesScratch
.
readBits
Long
(
15
)
<<
15
;
pts
|=
pesScratch
.
readBits
(
15
)
<<
15
;
pesScratch
.
skipBits
(
1
);
// marker_bit
pts
|=
pesScratch
.
readBits
Long
(
15
);
pts
|=
pesScratch
.
readBits
(
15
);
pesScratch
.
skipBits
(
1
);
// marker_bit
timeUs
=
ptsToTimeUs
(
pts
);
}
...
...
library/src/main/java/com/google/android/exoplayer/hls/HlsParserUtil.java
View file @
e3a7fc4d
...
...
@@ -51,7 +51,7 @@ import java.util.regex.Pattern;
public
static
String
parseOptionalStringAttr
(
String
line
,
Pattern
pattern
)
{
Matcher
matcher
=
pattern
.
matcher
(
line
);
if
(
matcher
.
find
()
&&
matcher
.
groupCount
()
==
1
)
{
if
(
matcher
.
find
())
{
return
matcher
.
group
(
1
);
}
return
null
;
...
...
@@ -59,7 +59,7 @@ import java.util.regex.Pattern;
public
static
boolean
parseOptionalBooleanAttr
(
String
line
,
Pattern
pattern
)
{
Matcher
matcher
=
pattern
.
matcher
(
line
);
if
(
matcher
.
find
()
&&
matcher
.
groupCount
()
==
1
)
{
if
(
matcher
.
find
())
{
return
BOOLEAN_YES
.
equals
(
matcher
.
group
(
1
));
}
return
false
;
...
...
library/src/main/java/com/google/android/exoplayer/hls/HlsPlaylistParser.java
View file @
e3a7fc4d
...
...
@@ -72,7 +72,7 @@ public final class HlsPlaylistParser implements UriLoadable.Parser<HlsPlaylist>
private
static
final
Pattern
CODECS_ATTR_REGEX
=
Pattern
.
compile
(
CODECS_ATTR
+
"=\"(.+?)\""
);
private
static
final
Pattern
RESOLUTION_ATTR_REGEX
=
Pattern
.
compile
(
RESOLUTION_ATTR
+
"=(\\d+
x\\d+
)"
);
Pattern
.
compile
(
RESOLUTION_ATTR
+
"=(\\d+
(\\.\\d+)?x\\d+(\\.\\d+)?
)"
);
private
static
final
Pattern
MEDIA_DURATION_REGEX
=
Pattern
.
compile
(
MEDIA_DURATION_TAG
+
":([\\d.]+),"
);
private
static
final
Pattern
MEDIA_SEQUENCE_REGEX
=
...
...
@@ -168,8 +168,16 @@ public final class HlsPlaylistParser implements UriLoadable.Parser<HlsPlaylist>
RESOLUTION_ATTR_REGEX
);
if
(
resolutionString
!=
null
)
{
String
[]
widthAndHeight
=
resolutionString
.
split
(
"x"
);
width
=
Integer
.
parseInt
(
widthAndHeight
[
0
]);
height
=
Integer
.
parseInt
(
widthAndHeight
[
1
]);
width
=
Math
.
round
(
Float
.
parseFloat
(
widthAndHeight
[
0
]));
if
(
width
<=
0
)
{
// Width was invalid.
width
=
-
1
;
}
height
=
Math
.
round
(
Float
.
parseFloat
(
widthAndHeight
[
1
]));
if
(
height
<=
0
)
{
// Height was invalid.
height
=
-
1
;
}
}
else
{
width
=
-
1
;
height
=
-
1
;
...
...
library/src/main/java/com/google/android/exoplayer/upstream/AssetDataSource.java
View file @
e3a7fc4d
...
...
@@ -16,7 +16,9 @@
package
com
.
google
.
android
.
exoplayer
.
upstream
;
import
com.google.android.exoplayer.C
;
import
com.google.android.exoplayer.util.Assertions
;
import
android.content.Context
;
import
android.content.res.AssetManager
;
import
java.io.EOFException
;
...
...
@@ -24,14 +26,14 @@ import java.io.IOException;
import
java.io.InputStream
;
/**
* A local asset {@link DataSource}.
* A local asset {@link
Uri
DataSource}.
*/
public
final
class
AssetDataSource
implements
DataSource
{
public
final
class
AssetDataSource
implements
Uri
DataSource
{
/**
* Thrown when
IOException is encountered during local asset read operation
.
* Thrown when
an {@link IOException} is encountered reading a local asset
.
*/
public
static
class
AssetDataSourceException
extends
IOException
{
public
static
final
class
AssetDataSourceException
extends
IOException
{
public
AssetDataSourceException
(
IOException
cause
)
{
super
(
cause
);
...
...
@@ -42,15 +44,16 @@ public final class AssetDataSource implements DataSource {
private
final
AssetManager
assetManager
;
private
final
TransferListener
listener
;
private
InputStream
assetInputStream
;
private
String
uriString
;
private
InputStream
inputStream
;
private
long
bytesRemaining
;
private
boolean
opened
;
/**
* Constructs a new {@link DataSource} that retrieves data from a local asset.
*/
public
AssetDataSource
(
AssetManager
assetManager
)
{
this
(
assetManager
,
null
);
public
AssetDataSource
(
Context
context
)
{
this
(
context
,
null
);
}
/**
...
...
@@ -58,19 +61,26 @@ public final class AssetDataSource implements DataSource {
*
* @param listener An optional listener. Specify {@code null} for no listener.
*/
public
AssetDataSource
(
AssetManager
assetManager
,
TransferListener
listener
)
{
this
.
assetManager
=
assetManager
;
public
AssetDataSource
(
Context
context
,
TransferListener
listener
)
{
this
.
assetManager
=
context
.
getAssets
()
;
this
.
listener
=
listener
;
}
@Override
public
long
open
(
DataSpec
dataSpec
)
throws
AssetDataSourceException
{
try
{
// Lose the '/' prefix in the path or else AssetManager won't find our file
assetInputStream
=
assetManager
.
open
(
dataSpec
.
uri
.
getPath
().
substring
(
1
),
AssetManager
.
ACCESS_RANDOM
);
assetInputStream
.
skip
(
dataSpec
.
position
);
bytesRemaining
=
dataSpec
.
length
==
C
.
LENGTH_UNBOUNDED
?
assetInputStream
.
available
()
uriString
=
dataSpec
.
uri
.
toString
();
String
path
=
dataSpec
.
uri
.
getPath
();
if
(
path
.
startsWith
(
"/android_asset/"
))
{
path
=
path
.
substring
(
15
);
}
else
if
(
path
.
startsWith
(
"/"
))
{
path
=
path
.
substring
(
1
);
}
uriString
=
dataSpec
.
uri
.
toString
();
inputStream
=
assetManager
.
open
(
path
,
AssetManager
.
ACCESS_RANDOM
);
long
skipped
=
inputStream
.
skip
(
dataSpec
.
position
);
Assertions
.
checkState
(
skipped
==
dataSpec
.
position
);
bytesRemaining
=
dataSpec
.
length
==
C
.
LENGTH_UNBOUNDED
?
inputStream
.
available
()
:
dataSpec
.
length
;
if
(
bytesRemaining
<
0
)
{
throw
new
EOFException
();
...
...
@@ -93,8 +103,7 @@ public final class AssetDataSource implements DataSource {
}
else
{
int
bytesRead
=
0
;
try
{
bytesRead
=
assetInputStream
.
read
(
buffer
,
offset
,
(
int
)
Math
.
min
(
bytesRemaining
,
readLength
));
bytesRead
=
inputStream
.
read
(
buffer
,
offset
,
(
int
)
Math
.
min
(
bytesRemaining
,
readLength
));
}
catch
(
IOException
e
)
{
throw
new
AssetDataSourceException
(
e
);
}
...
...
@@ -111,14 +120,20 @@ public final class AssetDataSource implements DataSource {
}
@Override
public
String
getUri
()
{
return
uriString
;
}
@Override
public
void
close
()
throws
AssetDataSourceException
{
if
(
assetInputStream
!=
null
)
{
uriString
=
null
;
if
(
inputStream
!=
null
)
{
try
{
assetI
nputStream
.
close
();
i
nputStream
.
close
();
}
catch
(
IOException
e
)
{
throw
new
AssetDataSourceException
(
e
);
}
finally
{
assetI
nputStream
=
null
;
i
nputStream
=
null
;
if
(
opened
)
{
opened
=
false
;
if
(
listener
!=
null
)
{
...
...
library/src/main/java/com/google/android/exoplayer/upstream/ContentDataSource.java
0 → 100644
View file @
e3a7fc4d
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
com
.
google
.
android
.
exoplayer
.
upstream
;
import
com.google.android.exoplayer.C
;
import
com.google.android.exoplayer.util.Assertions
;
import
android.content.ContentResolver
;
import
android.content.Context
;
import
android.content.res.AssetFileDescriptor
;
import
java.io.EOFException
;
import
java.io.FileInputStream
;
import
java.io.IOException
;
import
java.io.InputStream
;
/**
* A content URI {@link UriDataSource}.
*/
public
final
class
ContentDataSource
implements
UriDataSource
{
/**
* Thrown when an {@link IOException} is encountered reading from a content URI.
*/
public
static
class
ContentDataSourceException
extends
IOException
{
public
ContentDataSourceException
(
IOException
cause
)
{
super
(
cause
);
}
}
private
final
ContentResolver
resolver
;
private
final
TransferListener
listener
;
private
InputStream
inputStream
;
private
String
uriString
;
private
long
bytesRemaining
;
private
boolean
opened
;
/**
* Constructs a new {@link DataSource} that retrieves data from a content provider.
*/
public
ContentDataSource
(
Context
context
)
{
this
(
context
,
null
);
}
/**
* Constructs a new {@link DataSource} that retrieves data from a content provider.
*
* @param listener An optional listener. Specify {@code null} for no listener.
*/
public
ContentDataSource
(
Context
context
,
TransferListener
listener
)
{
this
.
resolver
=
context
.
getContentResolver
();
this
.
listener
=
listener
;
}
@Override
public
long
open
(
DataSpec
dataSpec
)
throws
ContentDataSourceException
{
try
{
uriString
=
dataSpec
.
uri
.
toString
();
AssetFileDescriptor
assetFd
=
resolver
.
openAssetFileDescriptor
(
dataSpec
.
uri
,
"r"
);
inputStream
=
new
FileInputStream
(
assetFd
.
getFileDescriptor
());
long
skipped
=
inputStream
.
skip
(
dataSpec
.
position
);
Assertions
.
checkState
(
skipped
==
dataSpec
.
position
);
bytesRemaining
=
dataSpec
.
length
==
C
.
LENGTH_UNBOUNDED
?
inputStream
.
available
()
:
dataSpec
.
length
;
if
(
bytesRemaining
<
0
)
{
throw
new
EOFException
();
}
}
catch
(
IOException
e
)
{
throw
new
ContentDataSourceException
(
e
);
}
opened
=
true
;
if
(
listener
!=
null
)
{
listener
.
onTransferStart
();
}
return
bytesRemaining
;
}
@Override
public
int
read
(
byte
[]
buffer
,
int
offset
,
int
readLength
)
throws
ContentDataSourceException
{
if
(
bytesRemaining
==
0
)
{
return
-
1
;
}
else
{
int
bytesRead
=
0
;
try
{
bytesRead
=
inputStream
.
read
(
buffer
,
offset
,
(
int
)
Math
.
min
(
bytesRemaining
,
readLength
));
}
catch
(
IOException
e
)
{
throw
new
ContentDataSourceException
(
e
);
}
if
(
bytesRead
>
0
)
{
bytesRemaining
-=
bytesRead
;
if
(
listener
!=
null
)
{
listener
.
onBytesTransferred
(
bytesRead
);
}
}
return
bytesRead
;
}
}
@Override
public
String
getUri
()
{
return
uriString
;
}
@Override
public
void
close
()
throws
ContentDataSourceException
{
uriString
=
null
;
if
(
inputStream
!=
null
)
{
try
{
inputStream
.
close
();
}
catch
(
IOException
e
)
{
throw
new
ContentDataSourceException
(
e
);
}
finally
{
inputStream
=
null
;
if
(
opened
)
{
opened
=
false
;
if
(
listener
!=
null
)
{
listener
.
onTransferEnd
();
}
}
}
}
}
}
library/src/main/java/com/google/android/exoplayer/upstream/DefaultBandwidthMeter.java
View file @
e3a7fc4d
...
...
@@ -28,7 +28,7 @@ import android.os.Handler;
*/
public
class
DefaultBandwidthMeter
implements
BandwidthMeter
{
p
rivate
static
final
int
DEFAULT_MAX_WEIGHT
=
2000
;
p
ublic
static
final
int
DEFAULT_MAX_WEIGHT
=
2000
;
private
final
Handler
eventHandler
;
private
final
EventListener
eventListener
;
...
...
library/src/main/java/com/google/android/exoplayer/upstream/DefaultUriDataSource.java
View file @
e3a7fc4d
...
...
@@ -17,17 +17,55 @@ package com.google.android.exoplayer.upstream;
import
com.google.android.exoplayer.util.Assertions
;
import
android.content.Context
;
import
android.text.TextUtils
;
import
java.io.IOException
;
/**
* A data source that fetches data from a local or remote {@link DataSpec}.
* A {@link UriDataSource} that supports multiple URI schemes. The supported schemes are:
*
* <ul>
* <li>http(s): For fetching data over HTTP and HTTPS (e.g. https://www.something.com/media.mp4).
* <li>file: For fetching data from a local file (e.g. file:///path/to/media/media.mp4, or just
* /path/to/media/media.mp4 because the implementation assumes that a URI without a scheme is a
* local file URI).
* <li>asset: For fetching data from an asset in the application's apk (e.g. asset:///media.mp4).
* <li>content: For fetching data from a content URI (e.g. content://authority/path/123).
* </ul>
*/
public
final
class
DefaultUriDataSource
implements
UriDataSource
{
private
static
final
String
FILE_URI_SCHEME
=
"file"
;
/**
* Thrown when a {@link DefaultUriDataSource} is opened for a URI with an unsupported scheme.
*/
public
static
final
class
UnsupportedSchemeException
extends
IOException
{
/**
* The unsupported scheme.
*/
public
final
String
scheme
;
/**
* @param scheme The unsupported scheme.
*/
public
UnsupportedSchemeException
(
String
scheme
)
{
super
(
"Unsupported URI scheme: "
+
scheme
);
this
.
scheme
=
scheme
;
}
}
private
static
final
String
SCHEME_HTTP
=
"http"
;
private
static
final
String
SCHEME_HTTPS
=
"https"
;
private
static
final
String
SCHEME_FILE
=
"file"
;
private
static
final
String
SCHEME_ASSET
=
"asset"
;
private
static
final
String
SCHEME_CONTENT
=
"content"
;
private
final
UriDataSource
fileDataSource
;
private
final
UriDataSource
httpDataSource
;
private
final
UriDataSource
fileDataSource
;
private
final
UriDataSource
assetDataSource
;
private
final
UriDataSource
contentDataSource
;
/**
* {@code null} if no data source is open. Otherwise, equal to {@link #fileDataSource} if the open
...
...
@@ -36,54 +74,89 @@ public final class DefaultUriDataSource implements UriDataSource {
private
UriDataSource
dataSource
;
/**
* Constructs a new data source that delegates to a {@link FileDataSource} for file URIs and a
* {@link DefaultHttpDataSource} for other URIs.
* Constructs a new instance.
* <p>
* The constructed instance will not follow cross-protocol redirects (i.e. redirects from HTTP to
* HTTPS or vice versa) when fetching remote data. Cross-protocol redirects can be enabled by
* using
the {@link #DefaultUriDataSource(String, TransferListener, boolean)} constructor and
*
passing
{@code true} as the final argument.
* using
{@link #DefaultUriDataSource(Context, TransferListener, String, boolean)} and passing
* {@code true} as the final argument.
*
* @param context A context.
* @param userAgent The User-Agent string that should be used when requesting remote data.
* @param transferListener An optional listener.
*/
public
DefaultUriDataSource
(
String
userAgent
,
TransferListener
transferListener
)
{
this
(
userAgent
,
transferListener
,
false
);
public
DefaultUriDataSource
(
Context
context
,
String
userAgent
)
{
this
(
context
,
null
,
userAgent
,
false
);
}
/**
* Constructs a new data source that delegates to a {@link FileDataSource} for file URIs and a
* {@link DefaultHttpDataSource} for other URIs.
* Constructs a new instance.
* <p>
* The constructed instance will not follow cross-protocol redirects (i.e. redirects from HTTP to
* HTTPS or vice versa) when fetching remote data. Cross-protocol redirects can be enabled by
* using {@link #DefaultUriDataSource(Context, TransferListener, String, boolean)} and passing
* {@code true} as the final argument.
*
* @param context A context.
* @param listener An optional {@link TransferListener}.
* @param userAgent The User-Agent string that should be used when requesting remote data.
*/
public
DefaultUriDataSource
(
Context
context
,
TransferListener
listener
,
String
userAgent
)
{
this
(
context
,
listener
,
userAgent
,
false
);
}
/**
* Constructs a new instance, optionally configured to follow cross-protocol redirects.
*
* @param context A context.
* @param listener An optional {@link TransferListener}.
* @param userAgent The User-Agent string that should be used when requesting remote data.
* @param transferListener An optional listener.
* @param allowCrossProtocolRedirects Whether cross-protocol redirects (i.e. redirects from HTTP
* to HTTPS and vice versa) are enabled when fetching remote data..
*/
public
DefaultUriDataSource
(
String
userAgent
,
TransferListener
transferListener
,
public
DefaultUriDataSource
(
Context
context
,
TransferListener
listener
,
String
userAgent
,
boolean
allowCrossProtocolRedirects
)
{
this
(
new
FileDataSource
(
transferListener
)
,
new
DefaultHttpDataSource
(
userAgent
,
null
,
transferL
istener
,
this
(
context
,
listener
,
new
DefaultHttpDataSource
(
userAgent
,
null
,
l
istener
,
DefaultHttpDataSource
.
DEFAULT_CONNECT_TIMEOUT_MILLIS
,
DefaultHttpDataSource
.
DEFAULT_READ_TIMEOUT_MILLIS
,
allowCrossProtocolRedirects
));
}
/**
* Constructs a new data source using {@code fileDataSource} for file URIs, and
* {@code httpDataSource} for non-file URIs.
* Constructs a new instance, using a provided {@link HttpDataSource} for fetching remote data.
*
* @param fileDataSource {@link UriDataSource} to use for file URIs.
* @param context A context.
* @param listener An optional {@link TransferListener}.
* @param httpDataSource {@link UriDataSource} to use for non-file URIs.
*/
public
DefaultUriDataSource
(
UriDataSource
fileDataSource
,
UriDataSource
httpDataSource
)
{
this
.
fileDataSource
=
Assertions
.
checkNotNull
(
fileDataSource
);
public
DefaultUriDataSource
(
Context
context
,
TransferListener
listener
,
UriDataSource
httpDataSource
)
{
this
.
httpDataSource
=
Assertions
.
checkNotNull
(
httpDataSource
);
this
.
fileDataSource
=
new
FileDataSource
(
listener
);
this
.
assetDataSource
=
new
AssetDataSource
(
context
,
listener
);
this
.
contentDataSource
=
new
ContentDataSource
(
context
,
listener
);
}
@Override
public
long
open
(
DataSpec
dataSpec
)
throws
IOException
{
Assertions
.
checkState
(
dataSource
==
null
);
dataSource
=
FILE_URI_SCHEME
.
equals
(
dataSpec
.
uri
.
getScheme
())
?
fileDataSource
:
httpDataSource
;
// Choose the correct source for the scheme.
String
scheme
=
dataSpec
.
uri
.
getScheme
();
if
(
SCHEME_HTTP
.
equals
(
scheme
)
||
SCHEME_HTTPS
.
equals
(
scheme
))
{
dataSource
=
httpDataSource
;
}
else
if
(
SCHEME_FILE
.
equals
(
scheme
)
||
TextUtils
.
isEmpty
(
scheme
))
{
if
(
dataSpec
.
uri
.
getPath
().
startsWith
(
"/android_asset/"
))
{
dataSource
=
assetDataSource
;
}
else
{
dataSource
=
fileDataSource
;
}
}
else
if
(
SCHEME_ASSET
.
equals
(
scheme
))
{
dataSource
=
assetDataSource
;
}
else
if
(
SCHEME_CONTENT
.
equals
(
scheme
))
{
dataSource
=
contentDataSource
;
}
else
{
throw
new
UnsupportedSchemeException
(
scheme
);
}
// Open the source and return.
return
dataSource
.
open
(
dataSpec
);
}
...
...
@@ -100,8 +173,11 @@ public final class DefaultUriDataSource implements UriDataSource {
@Override
public
void
close
()
throws
IOException
{
if
(
dataSource
!=
null
)
{
dataSource
.
close
();
dataSource
=
null
;
try
{
dataSource
.
close
();
}
finally
{
dataSource
=
null
;
}
}
}
...
...
library/src/main/java/com/google/android/exoplayer/upstream/FileDataSource.java
View file @
e3a7fc4d
...
...
@@ -22,7 +22,7 @@ import java.io.IOException;
import
java.io.RandomAccessFile
;
/**
* A local file {@link DataSource}.
* A local file {@link
Uri
DataSource}.
*/
public
final
class
FileDataSource
implements
UriDataSource
{
...
...
@@ -40,7 +40,7 @@ public final class FileDataSource implements UriDataSource {
private
final
TransferListener
listener
;
private
RandomAccessFile
file
;
private
String
uri
;
private
String
uri
String
;
private
long
bytesRemaining
;
private
boolean
opened
;
...
...
@@ -63,7 +63,7 @@ public final class FileDataSource implements UriDataSource {
@Override
public
long
open
(
DataSpec
dataSpec
)
throws
FileDataSourceException
{
try
{
uri
=
dataSpec
.
uri
.
toString
();
uri
String
=
dataSpec
.
uri
.
toString
();
file
=
new
RandomAccessFile
(
dataSpec
.
uri
.
getPath
(),
"r"
);
file
.
seek
(
dataSpec
.
position
);
bytesRemaining
=
dataSpec
.
length
==
C
.
LENGTH_UNBOUNDED
?
file
.
length
()
-
dataSpec
.
position
...
...
@@ -108,11 +108,12 @@ public final class FileDataSource implements UriDataSource {
@Override
public
String
getUri
()
{
return
uri
;
return
uri
String
;
}
@Override
public
void
close
()
throws
FileDataSourceException
{
uriString
=
null
;
if
(
file
!=
null
)
{
try
{
file
.
close
();
...
...
@@ -120,8 +121,6 @@ public final class FileDataSource implements UriDataSource {
throw
new
FileDataSourceException
(
e
);
}
finally
{
file
=
null
;
uri
=
null
;
if
(
opened
)
{
opened
=
false
;
if
(
listener
!=
null
)
{
...
...
library/src/main/java/com/google/android/exoplayer/upstream/HttpDataSource.java
View file @
e3a7fc4d
...
...
@@ -25,7 +25,7 @@ import java.util.List;
import
java.util.Map
;
/**
* An HTTP specific extension to {@link DataSource}.
* An HTTP specific extension to {@link
Uri
DataSource}.
*/
public
interface
HttpDataSource
extends
UriDataSource
{
...
...
library/src/main/java/com/google/android/exoplayer/util/ParsableBitArray.java
View file @
e3a7fc4d
...
...
@@ -99,21 +99,11 @@ public final class ParsableBitArray {
* @return An integer whose bottom n bits hold the read data.
*/
public
int
readBits
(
int
n
)
{
return
(
int
)
readBitsLong
(
n
);
}
/**
* Reads up to 64 bits.
*
* @param n The number of bits to read.
* @return A long whose bottom n bits hold the read data.
*/
public
long
readBitsLong
(
int
n
)
{
if
(
n
==
0
)
{
return
0
;
}
long
retval
=
0
;
int
retval
=
0
;
// While n >= 8, read whole bytes.
while
(
n
>=
8
)
{
...
...
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