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
dd41d14a
authored
May 11, 2015
by
ojw28
Browse files
Options
_('Browse Files')
Download
Plain Diff
Merge pull request #444 from google/dev
dev -> dev-webm-vp9-opus
parents
5d92cc83
166c2f7c
Hide whitespace changes
Inline
Side-by-side
Showing
45 changed files
with
452 additions
and
210 deletions
build.gradle
demo/build.gradle
demo/src/main/AndroidManifest.xml
demo/src/main/java/com/google/android/exoplayer/demo/DemoUtil.java
demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java
demo/src/main/java/com/google/android/exoplayer/demo/Samples.java
demo/src/main/java/com/google/android/exoplayer/demo/WidevineTestMediaDrmCallback.java
demo/src/main/java/com/google/android/exoplayer/demo/player/DashRendererBuilder.java
demo/src/main/java/com/google/android/exoplayer/demo/player/DebugTrackRenderer.java
demo/src/main/java/com/google/android/exoplayer/demo/player/HlsRendererBuilder.java
demo/src/main/project.properties
library/build.gradle
library/src/main/AndroidManifest.xml
library/src/main/java/com/google/android/exoplayer/DummyTrackRenderer.java
library/src/main/java/com/google/android/exoplayer/ExoPlayer.java
library/src/main/java/com/google/android/exoplayer/ExoPlayerImplInternal.java
library/src/main/java/com/google/android/exoplayer/FrameworkSampleSource.java
library/src/main/java/com/google/android/exoplayer/MediaCodecAudioTrackRenderer.java
library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java
library/src/main/java/com/google/android/exoplayer/MediaCodecVideoTrackRenderer.java
library/src/main/java/com/google/android/exoplayer/SampleSource.java
library/src/main/java/com/google/android/exoplayer/TrackRenderer.java
library/src/main/java/com/google/android/exoplayer/audio/AudioTrack.java
library/src/main/java/com/google/android/exoplayer/chunk/ChunkSampleSource.java
library/src/main/java/com/google/android/exoplayer/chunk/FormatEvaluator.java
library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java
library/src/main/java/com/google/android/exoplayer/dash/mpd/MediaPresentationDescriptionParser.java
library/src/main/java/com/google/android/exoplayer/extractor/ExtractorSampleSource.java
library/src/main/java/com/google/android/exoplayer/extractor/mp3/XingSeeker.java
library/src/main/java/com/google/android/exoplayer/extractor/mp4/AtomParsers.java
library/src/main/java/com/google/android/exoplayer/extractor/ts/TsExtractor.java
library/src/main/java/com/google/android/exoplayer/hls/HlsChunkSource.java
library/src/main/java/com/google/android/exoplayer/hls/HlsSampleSource.java
library/src/main/java/com/google/android/exoplayer/metadata/MetadataTrackRenderer.java
library/src/main/java/com/google/android/exoplayer/text/TextTrackRenderer.java
library/src/main/java/com/google/android/exoplayer/text/eia608/Eia608TrackRenderer.java
library/src/main/java/com/google/android/exoplayer/upstream/BandwidthMeter.java
library/src/main/java/com/google/android/exoplayer/upstream/DefaultBandwidthMeter.java
library/src/main/java/com/google/android/exoplayer/upstream/DefaultHttpDataSource.java
library/src/main/java/com/google/android/exoplayer/upstream/DefaultUriDataSource.java
library/src/main/java/com/google/android/exoplayer/util/MimeTypes.java
library/src/main/project.properties
library/src/test/AndroidManifest.xml
library/src/test/java/com/google/android/exoplayer/extractor/mp4/Mp4ExtractorTest.java
library/src/test/project.properties
build.gradle
View file @
dd41d14a
...
...
@@ -20,7 +20,7 @@ buildscript {
jcenter
()
}
dependencies
{
classpath
'com.android.tools.build:gradle:1.
0.0
'
classpath
'com.android.tools.build:gradle:1.
2.2
'
classpath
'com.novoda:bintray-release:0.2.7'
}
}
...
...
demo/build.gradle
View file @
dd41d14a
...
...
@@ -14,12 +14,12 @@
apply
plugin:
'com.android.application'
android
{
compileSdkVersion
2
1
buildToolsVersion
"2
1.1.2
"
compileSdkVersion
2
2
buildToolsVersion
"2
2.0.1
"
defaultConfig
{
minSdkVersion
16
targetSdkVersion
2
1
targetSdkVersion
2
2
}
buildTypes
{
release
{
...
...
demo/src/main/AndroidManifest.xml
View file @
dd41d14a
...
...
@@ -25,7 +25,7 @@
<uses-permission
android:name=
"android.permission.ACCESS_WIFI_STATE"
/>
<uses-permission
android:name=
"android.permission.WRITE_EXTERNAL_STORAGE"
/>
<uses-sdk
android:minSdkVersion=
"16"
android:targetSdkVersion=
"2
1
"
/>
<uses-sdk
android:minSdkVersion=
"16"
android:targetSdkVersion=
"2
2
"
/>
<application
android:label=
"@string/application_name"
...
...
demo/src/main/java/com/google/android/exoplayer/demo/DemoUtil.java
View file @
dd41d14a
...
...
@@ -25,7 +25,6 @@ import java.net.CookieHandler;
import
java.net.CookieManager
;
import
java.net.CookiePolicy
;
import
java.net.HttpURLConnection
;
import
java.net.MalformedURLException
;
import
java.net.URL
;
import
java.util.Map
;
...
...
@@ -52,7 +51,7 @@ public class DemoUtil {
}
public
static
byte
[]
executePost
(
String
url
,
byte
[]
data
,
Map
<
String
,
String
>
requestProperties
)
throws
MalformedURLException
,
IOException
{
throws
IOException
{
HttpURLConnection
urlConnection
=
null
;
try
{
urlConnection
=
(
HttpURLConnection
)
new
URL
(
url
).
openConnection
();
...
...
demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java
View file @
dd41d14a
...
...
@@ -229,7 +229,8 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
return
new
DashRendererBuilder
(
this
,
userAgent
,
contentUri
.
toString
(),
new
WidevineTestMediaDrmCallback
(
contentId
),
debugTextView
,
audioCapabilities
);
case
DemoUtil
.
TYPE_HLS
:
return
new
HlsRendererBuilder
(
this
,
userAgent
,
contentUri
.
toString
(),
debugTextView
);
return
new
HlsRendererBuilder
(
this
,
userAgent
,
contentUri
.
toString
(),
debugTextView
,
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
,
...
...
@@ -239,7 +240,7 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
new
Mp3Extractor
());
case
DemoUtil
.
TYPE_TS
:
return
new
ExtractorRendererBuilder
(
userAgent
,
contentUri
,
debugTextView
,
new
TsExtractor
());
new
TsExtractor
(
0
,
audioCapabilities
));
case
DemoUtil
.
TYPE_AAC
:
return
new
ExtractorRendererBuilder
(
userAgent
,
contentUri
,
debugTextView
,
new
AdtsExtractor
());
...
...
demo/src/main/java/com/google/android/exoplayer/demo/Samples.java
View file @
dd41d14a
...
...
@@ -130,8 +130,6 @@ import java.util.Locale;
public
static
final
Sample
[]
MISC
=
new
Sample
[]
{
new
Sample
(
"Dizzy"
,
"http://html5demos.com/assets/dizzy.mp4"
,
DemoUtil
.
TYPE_MP4
),
new
Sample
(
"Dizzy (https->http redirect)"
,
"https://goo.gl/MtUDEj"
,
DemoUtil
.
TYPE_MP4
),
new
Sample
(
"Apple AAC 10s"
,
"https://devimages.apple.com.edgekey.net/"
+
"streaming/examples/bipbop_4x3/gear0/fileSequence0.aac"
,
DemoUtil
.
TYPE_AAC
),
...
...
demo/src/main/java/com/google/android/exoplayer/demo/WidevineTestMediaDrmCallback.java
View file @
dd41d14a
...
...
@@ -22,8 +22,6 @@ import android.media.MediaDrm.KeyRequest;
import
android.media.MediaDrm.ProvisionRequest
;
import
android.text.TextUtils
;
import
org.apache.http.client.ClientProtocolException
;
import
java.io.IOException
;
import
java.util.UUID
;
...
...
@@ -43,8 +41,7 @@ public class WidevineTestMediaDrmCallback implements MediaDrmCallback {
}
@Override
public
byte
[]
executeProvisionRequest
(
UUID
uuid
,
ProvisionRequest
request
)
throws
ClientProtocolException
,
IOException
{
public
byte
[]
executeProvisionRequest
(
UUID
uuid
,
ProvisionRequest
request
)
throws
IOException
{
String
url
=
request
.
getDefaultUrl
()
+
"&signedRequest="
+
new
String
(
request
.
getData
());
return
DemoUtil
.
executePost
(
url
,
null
,
null
);
}
...
...
demo/src/main/java/com/google/android/exoplayer/demo/player/DashRendererBuilder.java
View file @
dd41d14a
...
...
@@ -59,7 +59,6 @@ import com.google.android.exoplayer.util.Util;
import
android.annotation.TargetApi
;
import
android.content.Context
;
import
android.media.AudioFormat
;
import
android.media.MediaCodec
;
import
android.media.UnsupportedSchemeException
;
import
android.os.Handler
;
...
...
@@ -249,7 +248,6 @@ public class DashRendererBuilder implements RendererBuilder,
// Build the audio chunk sources.
List
<
ChunkSource
>
audioChunkSourceList
=
new
ArrayList
<
ChunkSource
>();
List
<
String
>
audioTrackNameList
=
new
ArrayList
<
String
>();
int
audioEncoding
=
AudioFormat
.
ENCODING_PCM_16BIT
;
if
(
audioAdaptationSet
!=
null
)
{
DataSource
audioDataSource
=
new
DefaultUriDataSource
(
userAgent
,
bandwidthMeter
);
FormatEvaluator
audioEvaluator
=
new
FormatEvaluator
.
FixedEvaluator
();
...
...
@@ -275,7 +273,6 @@ public class DashRendererBuilder implements RendererBuilder,
continue
;
}
audioEncoding
=
encoding
;
for
(
int
j
=
audioRepresentations
.
size
()
-
1
;
j
>=
0
;
j
--)
{
if
(!
audioRepresentations
.
get
(
j
).
format
.
codecs
.
equals
(
codec
))
{
audioTrackNameList
.
remove
(
j
);
...
...
@@ -303,7 +300,7 @@ public class DashRendererBuilder implements RendererBuilder,
AUDIO_BUFFER_SEGMENTS
*
BUFFER_SEGMENT_SIZE
,
true
,
mainHandler
,
player
,
DemoPlayer
.
TYPE_AUDIO
);
audioRenderer
=
new
MediaCodecAudioTrackRenderer
(
audioSampleSource
,
drmSessionManager
,
true
,
mainHandler
,
player
,
audioEncoding
);
mainHandler
,
player
);
}
// Build the text chunk sources.
...
...
demo/src/main/java/com/google/android/exoplayer/demo/player/DebugTrackRenderer.java
View file @
dd41d14a
...
...
@@ -57,7 +57,7 @@ import android.widget.TextView;
}
@Override
protected
int
doPrepare
()
throws
ExoPlaybackException
{
protected
int
doPrepare
(
long
positionUs
)
throws
ExoPlaybackException
{
maybeFail
();
return
STATE_PREPARED
;
}
...
...
demo/src/main/java/com/google/android/exoplayer/demo/player/HlsRendererBuilder.java
View file @
dd41d14a
...
...
@@ -19,6 +19,7 @@ import com.google.android.exoplayer.MediaCodecAudioTrackRenderer;
import
com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException
;
import
com.google.android.exoplayer.MediaCodecVideoTrackRenderer
;
import
com.google.android.exoplayer.TrackRenderer
;
import
com.google.android.exoplayer.audio.AudioCapabilities
;
import
com.google.android.exoplayer.chunk.VideoFormatSelectorUtil
;
import
com.google.android.exoplayer.demo.player.DemoPlayer.RendererBuilder
;
import
com.google.android.exoplayer.demo.player.DemoPlayer.RendererBuilderCallback
;
...
...
@@ -56,15 +57,18 @@ public class HlsRendererBuilder implements RendererBuilder, ManifestCallback<Hls
private
final
String
userAgent
;
private
final
String
url
;
private
final
TextView
debugTextView
;
private
final
AudioCapabilities
audioCapabilities
;
private
DemoPlayer
player
;
private
RendererBuilderCallback
callback
;
public
HlsRendererBuilder
(
Context
context
,
String
userAgent
,
String
url
,
TextView
debugTextView
)
{
public
HlsRendererBuilder
(
Context
context
,
String
userAgent
,
String
url
,
TextView
debugTextView
,
AudioCapabilities
audioCapabilities
)
{
this
.
context
=
context
;
this
.
userAgent
=
userAgent
;
this
.
url
=
url
;
this
.
debugTextView
=
debugTextView
;
this
.
audioCapabilities
=
audioCapabilities
;
}
@Override
...
...
@@ -101,7 +105,7 @@ public class HlsRendererBuilder implements RendererBuilder, ManifestCallback<Hls
DataSource
dataSource
=
new
DefaultUriDataSource
(
userAgent
,
bandwidthMeter
);
HlsChunkSource
chunkSource
=
new
HlsChunkSource
(
dataSource
,
url
,
manifest
,
bandwidthMeter
,
variantIndices
,
HlsChunkSource
.
ADAPTIVE_MODE_SPLICE
);
variantIndices
,
HlsChunkSource
.
ADAPTIVE_MODE_SPLICE
,
audioCapabilities
);
HlsSampleSource
sampleSource
=
new
HlsSampleSource
(
chunkSource
,
true
,
3
,
REQUESTED_BUFFER_SIZE
,
REQUESTED_BUFFER_DURATION_MS
,
mainHandler
,
player
,
DemoPlayer
.
TYPE_VIDEO
);
MediaCodecVideoTrackRenderer
videoRenderer
=
new
MediaCodecVideoTrackRenderer
(
sampleSource
,
...
...
demo/src/main/project.properties
View file @
dd41d14a
...
...
@@ -8,6 +8,6 @@
# project structure.
# Project target.
target
=
android-2
1
target
=
android-2
2
android.library
=
false
android.library.reference.1
=
../../../library/src/main
library/build.gradle
View file @
dd41d14a
...
...
@@ -15,8 +15,8 @@ apply plugin: 'com.android.library'
apply
plugin:
'bintray-release'
android
{
compileSdkVersion
2
1
buildToolsVersion
"2
1.1.2
"
compileSdkVersion
2
2
buildToolsVersion
"2
2.0.1
"
defaultConfig
{
// Important: ExoPlayerLib specifies a minSdkVersion of 9 because
...
...
@@ -25,7 +25,7 @@ android {
// functionality provided by the library requires API level 16 or
// greater.
minSdkVersion
9
targetSdkVersion
2
1
targetSdkVersion
2
2
}
buildTypes
{
...
...
library/src/main/AndroidManifest.xml
View file @
dd41d14a
...
...
@@ -27,6 +27,6 @@
the library may be of use on older devices. However, please note that the core video playback
functionality provided by the library requires API level 16 or greater.
-->
<uses-sdk
android:minSdkVersion=
"9"
android:targetSdkVersion=
"2
1
"
/>
<uses-sdk
android:minSdkVersion=
"9"
android:targetSdkVersion=
"2
2
"
/>
</manifest>
library/src/main/java/com/google/android/exoplayer/DummyTrackRenderer.java
View file @
dd41d14a
...
...
@@ -18,14 +18,14 @@ package com.google.android.exoplayer;
/**
* A {@link TrackRenderer} that does nothing.
* <p>
* This renderer returns {@link TrackRenderer#STATE_IGNORE} from {@link #doPrepare(
)} in order to
*
request that it should be ignored. {@link IllegalStateException} is thrown from all methods that
* are documented to indicate that they should not be invoked unless the renderer is prepared.
* This renderer returns {@link TrackRenderer#STATE_IGNORE} from {@link #doPrepare(
long)} in order
*
to request that it should be ignored. {@link IllegalStateException} is thrown from all methods
*
that
are documented to indicate that they should not be invoked unless the renderer is prepared.
*/
public
class
DummyTrackRenderer
extends
TrackRenderer
{
@Override
protected
int
doPrepare
()
throws
ExoPlaybackException
{
protected
int
doPrepare
(
long
positionUs
)
throws
ExoPlaybackException
{
return
STATE_IGNORE
;
}
...
...
library/src/main/java/com/google/android/exoplayer/ExoPlayer.java
View file @
dd41d14a
...
...
@@ -101,7 +101,7 @@ public interface ExoPlayer {
* The default minimum duration of data that must be buffered for playback to start or resume
* following a user action such as a seek.
*/
public
static
final
int
DEFAULT_MIN_BUFFER_MS
=
500
;
public
static
final
int
DEFAULT_MIN_BUFFER_MS
=
2
500
;
/**
* The default minimum duration of data that must be buffered for playback to resume
...
...
library/src/main/java/com/google/android/exoplayer/ExoPlayerImplInternal.java
View file @
dd41d14a
...
...
@@ -264,7 +264,7 @@ import java.util.List;
boolean
prepared
=
true
;
for
(
int
i
=
0
;
i
<
renderers
.
length
;
i
++)
{
if
(
renderers
[
i
].
getState
()
==
TrackRenderer
.
STATE_UNPREPARED
)
{
int
state
=
renderers
[
i
].
prepare
();
int
state
=
renderers
[
i
].
prepare
(
positionUs
);
if
(
state
==
TrackRenderer
.
STATE_UNPREPARED
)
{
prepared
=
false
;
}
...
...
library/src/main/java/com/google/android/exoplayer/FrameworkSampleSource.java
View file @
dd41d14a
...
...
@@ -128,7 +128,7 @@ public final class FrameworkSampleSource implements SampleSource {
}
@Override
public
boolean
prepare
()
throws
IOException
{
public
boolean
prepare
(
long
positionUs
)
throws
IOException
{
if
(!
prepared
)
{
extractor
=
new
MediaExtractor
();
if
(
context
!=
null
)
{
...
...
library/src/main/java/com/google/android/exoplayer/MediaCodecAudioTrackRenderer.java
View file @
dd41d14a
...
...
@@ -21,9 +21,7 @@ import com.google.android.exoplayer.drm.DrmSessionManager;
import
com.google.android.exoplayer.util.MimeTypes
;
import
android.annotation.TargetApi
;
import
android.media.AudioFormat
;
import
android.media.MediaCodec
;
import
android.media.MediaFormat
;
import
android.media.audiofx.Virtualizer
;
import
android.os.Handler
;
...
...
@@ -71,7 +69,6 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer {
private
final
EventListener
eventListener
;
private
final
AudioTrack
audioTrack
;
private
final
int
encoding
;
private
int
audioSessionId
;
private
long
currentPositionUs
;
...
...
@@ -124,50 +121,27 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer {
*/
public
MediaCodecAudioTrackRenderer
(
SampleSource
source
,
DrmSessionManager
drmSessionManager
,
boolean
playClearSamplesWithoutKeys
,
Handler
eventHandler
,
EventListener
eventListener
)
{
this
(
source
,
drmSessionManager
,
playClearSamplesWithoutKeys
,
eventHandler
,
eventListener
,
AudioFormat
.
ENCODING_PCM_16BIT
);
}
/**
* @param source The upstream source from which the renderer obtains samples.
* @param drmSessionManager For use with encrypted content. May be null if support for encrypted
* content is not required.
* @param playClearSamplesWithoutKeys Encrypted media may contain clear (un-encrypted) regions.
* For example a media file may start with a short clear region so as to allow playback to
* begin in parallel with key acquisision. This parameter specifies whether the renderer is
* permitted to play clear regions of encrypted media files before {@code drmSessionManager}
* has obtained the keys necessary to decrypt encrypted regions of the media.
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
* null if delivery of events is not required.
* @param eventListener A listener of events. May be null if delivery of events is not required.
* @param encoding One of the {@code AudioFormat.ENCODING_*} constants specifying the audio
* encoding.
*/
public
MediaCodecAudioTrackRenderer
(
SampleSource
source
,
DrmSessionManager
drmSessionManager
,
boolean
playClearSamplesWithoutKeys
,
Handler
eventHandler
,
EventListener
eventListener
,
int
encoding
)
{
super
(
source
,
drmSessionManager
,
playClearSamplesWithoutKeys
,
eventHandler
,
eventListener
);
this
.
eventListener
=
eventListener
;
this
.
audioSessionId
=
AudioTrack
.
SESSION_ID_NOT_SET
;
this
.
audioTrack
=
new
AudioTrack
();
this
.
encoding
=
encoding
;
}
@Override
protected
DecoderInfo
getDecoderInfo
(
String
mimeType
,
boolean
requiresSecureDecoder
)
throws
DecoderQueryException
{
if
(
encoding
==
AudioFormat
.
ENCODING_AC3
||
encoding
==
AudioFormat
.
ENCODING_E_AC3
)
{
if
(
MimeTypes
.
isPassthroughAudio
(
mimeType
)
)
{
return
new
DecoderInfo
(
RAW_DECODER_NAME
,
true
);
}
return
super
.
getDecoderInfo
(
mimeType
,
requiresSecureDecoder
);
}
@Override
protected
void
configureCodec
(
MediaCodec
codec
,
String
codecName
,
MediaFormat
format
,
android
.
media
.
MediaCrypto
crypto
)
{
protected
void
configureCodec
(
MediaCodec
codec
,
String
codecName
,
android
.
media
.
Media
Format
format
,
android
.
media
.
Media
Crypto
crypto
)
{
if
(
RAW_DECODER_NAME
.
equals
(
codecName
))
{
// Override the MIME type used to configure the codec if we are using a passthrough decoder.
String
mimeType
=
format
.
getString
(
MediaFormat
.
KEY_MIME
);
String
mimeType
=
format
.
getString
(
android
.
media
.
MediaFormat
.
KEY_MIME
);
format
.
setString
(
android
.
media
.
MediaFormat
.
KEY_MIME
,
MimeTypes
.
AUDIO_RAW
);
codec
.
configure
(
format
,
null
,
crypto
,
0
);
format
.
setString
(
android
.
media
.
MediaFormat
.
KEY_MIME
,
mimeType
);
...
...
@@ -193,8 +167,13 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer {
}
@Override
protected
void
onOutputFormatChanged
(
MediaFormat
format
)
{
audioTrack
.
reconfigure
(
format
,
encoding
,
0
);
protected
void
onOutputFormatChanged
(
MediaFormat
inputFormat
,
android
.
media
.
MediaFormat
outputFormat
)
{
if
(
MimeTypes
.
isPassthroughAudio
(
inputFormat
.
mimeType
))
{
audioTrack
.
reconfigure
(
inputFormat
.
getFrameworkMediaFormatV16
());
}
else
{
audioTrack
.
reconfigure
(
outputFormat
);
}
}
/**
...
...
library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java
View file @
dd41d14a
...
...
@@ -243,9 +243,9 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
}
@Override
protected
int
doPrepare
()
throws
ExoPlaybackException
{
protected
int
doPrepare
(
long
positionUs
)
throws
ExoPlaybackException
{
try
{
boolean
sourcePrepared
=
source
.
prepare
();
boolean
sourcePrepared
=
source
.
prepare
(
positionUs
);
if
(!
sourcePrepared
)
{
return
TrackRenderer
.
STATE_UNPREPARED
;
}
...
...
@@ -742,9 +742,11 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
* <p>
* The default implementation is a no-op.
*
* @param format The new output format.
* @param inputFormat The format of media input to the codec.
* @param outputFormat The new output format.
*/
protected
void
onOutputFormatChanged
(
android
.
media
.
MediaFormat
format
)
{
protected
void
onOutputFormatChanged
(
MediaFormat
inputFormat
,
android
.
media
.
MediaFormat
outputFormat
)
{
// Do nothing.
}
...
...
@@ -818,7 +820,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
}
if
(
outputIndex
==
MediaCodec
.
INFO_OUTPUT_FORMAT_CHANGED
)
{
onOutputFormatChanged
(
codec
.
getOutputFormat
());
onOutputFormatChanged
(
format
,
codec
.
getOutputFormat
());
codecCounters
.
outputFormatChangedCount
++;
return
true
;
}
else
if
(
outputIndex
==
MediaCodec
.
INFO_OUTPUT_BUFFERS_CHANGED
)
{
...
...
library/src/main/java/com/google/android/exoplayer/MediaCodecVideoTrackRenderer.java
View file @
dd41d14a
...
...
@@ -381,15 +381,17 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
}
@Override
protected
void
onOutputFormatChanged
(
android
.
media
.
MediaFormat
format
)
{
boolean
hasCrop
=
format
.
containsKey
(
KEY_CROP_RIGHT
)
&&
format
.
containsKey
(
KEY_CROP_LEFT
)
&&
format
.
containsKey
(
KEY_CROP_BOTTOM
)
&&
format
.
containsKey
(
KEY_CROP_TOP
);
protected
void
onOutputFormatChanged
(
MediaFormat
inputFormat
,
android
.
media
.
MediaFormat
outputFormat
)
{
boolean
hasCrop
=
outputFormat
.
containsKey
(
KEY_CROP_RIGHT
)
&&
outputFormat
.
containsKey
(
KEY_CROP_LEFT
)
&&
outputFormat
.
containsKey
(
KEY_CROP_BOTTOM
)
&&
outputFormat
.
containsKey
(
KEY_CROP_TOP
);
currentWidth
=
hasCrop
?
format
.
getInteger
(
KEY_CROP_RIGHT
)
-
f
ormat
.
getInteger
(
KEY_CROP_LEFT
)
+
1
:
f
ormat
.
getInteger
(
android
.
media
.
MediaFormat
.
KEY_WIDTH
);
?
outputFormat
.
getInteger
(
KEY_CROP_RIGHT
)
-
outputF
ormat
.
getInteger
(
KEY_CROP_LEFT
)
+
1
:
outputF
ormat
.
getInteger
(
android
.
media
.
MediaFormat
.
KEY_WIDTH
);
currentHeight
=
hasCrop
?
format
.
getInteger
(
KEY_CROP_BOTTOM
)
-
f
ormat
.
getInteger
(
KEY_CROP_TOP
)
+
1
:
f
ormat
.
getInteger
(
android
.
media
.
MediaFormat
.
KEY_HEIGHT
);
?
outputFormat
.
getInteger
(
KEY_CROP_BOTTOM
)
-
outputF
ormat
.
getInteger
(
KEY_CROP_TOP
)
+
1
:
outputF
ormat
.
getInteger
(
android
.
media
.
MediaFormat
.
KEY_HEIGHT
);
}
@Override
...
...
library/src/main/java/com/google/android/exoplayer/SampleSource.java
View file @
dd41d14a
...
...
@@ -57,10 +57,11 @@ public interface SampleSource {
* and formats). If insufficient data is available then the call will return {@code false} rather
* than block. The method can be called repeatedly until the return value indicates success.
*
* @param positionUs The player's current playback position.
* @return True if the source was prepared successfully, false otherwise.
* @throws IOException If an error occurred preparing the source.
*/
public
boolean
prepare
()
throws
IOException
;
public
boolean
prepare
(
long
positionUs
)
throws
IOException
;
/**
* Returns the number of tracks exposed by the source.
...
...
library/src/main/java/com/google/android/exoplayer/TrackRenderer.java
View file @
dd41d14a
...
...
@@ -108,11 +108,12 @@ public abstract class TrackRenderer implements ExoPlayerComponent {
* Prepares the renderer. This method is non-blocking, and hence it may be necessary to call it
* more than once in order to transition the renderer into the prepared state.
*
* @param positionUs The player's current playback position.
* @return The current state (one of the STATE_* constants), for convenience.
*/
/* package */
final
int
prepare
()
throws
ExoPlaybackException
{
/* package */
final
int
prepare
(
long
positionUs
)
throws
ExoPlaybackException
{
Assertions
.
checkState
(
state
==
TrackRenderer
.
STATE_UNPREPARED
);
state
=
doPrepare
();
state
=
doPrepare
(
positionUs
);
Assertions
.
checkState
(
state
==
TrackRenderer
.
STATE_UNPREPARED
||
state
==
TrackRenderer
.
STATE_PREPARED
||
state
==
TrackRenderer
.
STATE_IGNORE
);
...
...
@@ -127,11 +128,12 @@ public abstract class TrackRenderer implements ExoPlayerComponent {
* This method should return quickly, and should not block if the renderer is currently unable to
* make any useful progress.
*
* @param positionUs The player's current playback position.
* @return The new state of the renderer. One of {@link #STATE_UNPREPARED},
* {@link #STATE_PREPARED} and {@link #STATE_IGNORE}.
* @throws ExoPlaybackException If an error occurs.
*/
protected
abstract
int
doPrepare
()
throws
ExoPlaybackException
;
protected
abstract
int
doPrepare
(
long
positionUs
)
throws
ExoPlaybackException
;
/**
* Enable the renderer.
...
...
library/src/main/java/com/google/android/exoplayer/audio/AudioTrack.java
View file @
dd41d14a
...
...
@@ -18,6 +18,7 @@ package com.google.android.exoplayer.audio;
import
com.google.android.exoplayer.C
;
import
com.google.android.exoplayer.util.Ac3Util
;
import
com.google.android.exoplayer.util.Assertions
;
import
com.google.android.exoplayer.util.MimeTypes
;
import
com.google.android.exoplayer.util.Util
;
import
android.annotation.TargetApi
;
...
...
@@ -315,24 +316,21 @@ public final class AudioTrack {
}
/**
* Reconfigures the audio track to play back media in {@code format}
. The encoding is assumed to
*
be {@link AudioFormat#ENCODING_PCM_16BIT}
.
* Reconfigures the audio track to play back media in {@code format}
, inferring a buffer size from
*
the format
.
*/
public
void
reconfigure
(
MediaFormat
format
)
{
reconfigure
(
format
,
AudioFormat
.
ENCODING_PCM_16BIT
,
0
);
reconfigure
(
format
,
0
);
}
/**
* Reconfigures the audio track to play back media in {@code format}. Buffers passed to
* {@link #handleBuffer} must use the specified {@code encoding}, which should be a constant from
* {@link AudioFormat}.
* Reconfigures the audio track to play back media in {@code format}.
*
* @param format Specifies the channel count and sample rate to play back.
* @param encoding The format in which audio is represented.
* @param specifiedBufferSize A specific size for the playback buffer in bytes, or 0 to use a
* size inferred from the format.
*/
public
void
reconfigure
(
MediaFormat
format
,
int
encoding
,
int
specifiedBufferSize
)
{
public
void
reconfigure
(
MediaFormat
format
,
int
specifiedBufferSize
)
{
int
channelCount
=
format
.
getInteger
(
MediaFormat
.
KEY_CHANNEL_COUNT
);
int
channelConfig
;
switch
(
channelCount
)
{
...
...
@@ -353,8 +351,10 @@ public final class AudioTrack {
}
int
sampleRate
=
format
.
getInteger
(
MediaFormat
.
KEY_SAMPLE_RATE
);
String
mimeType
=
format
.
getString
(
MediaFormat
.
KEY_MIME
);
// TODO: Does channelConfig determine channelCount?
int
encoding
=
MimeTypes
.
getEncodingForMimeType
(
mimeType
);
boolean
isAc3
=
encoding
==
C
.
ENCODING_AC3
||
encoding
==
C
.
ENCODING_E_AC3
;
if
(
isInitialized
()
&&
this
.
sampleRate
==
sampleRate
&&
this
.
channelConfig
==
channelConfig
&&
!
this
.
isAc3
&&
!
isAc3
)
{
...
...
@@ -423,10 +423,21 @@ public final class AudioTrack {
return
RESULT_BUFFER_CONSUMED
;
}
// As a workaround for an issue where an an AC-3 audio track continues to play data written
// while it is paused, stop writing so its buffer empties. See [Internal: b/18899620].
if
(
isAc3
&&
audioTrack
.
getPlayState
()
==
android
.
media
.
AudioTrack
.
PLAYSTATE_PAUSED
)
{
return
0
;
// Workarounds for issues with AC-3 passthrough AudioTracks on API versions 21/22:
if
(
Util
.
SDK_INT
<=
22
&&
isAc3
)
{
// An AC-3 audio track continues to play data written while it is paused. Stop writing so its
// buffer empties. See [Internal: b/18899620].
if
(
audioTrack
.
getPlayState
()
==
android
.
media
.
AudioTrack
.
PLAYSTATE_PAUSED
)
{
return
0
;
}
// A new AC-3 audio track's playback position continues to increase from the old track's
// position for a short time after is has been released. Avoid writing data until the playback
// head position actually returns to zero.
if
(
audioTrack
.
getPlayState
()
==
android
.
media
.
AudioTrack
.
PLAYSTATE_STOPPED
&&
audioTrackUtil
.
getPlaybackHeadPosition
()
!=
0
)
{
return
0
;
}
}
int
result
=
0
;
...
...
@@ -637,7 +648,8 @@ public final class AudioTrack {
}
if
(
systemClockUs
-
lastTimestampSampleTimeUs
>=
MIN_TIMESTAMP_SAMPLE_INTERVAL_US
)
{
audioTimestampSet
=
audioTrackUtil
.
updateTimestamp
();
// Don't use AudioTrack.getTimestamp() on AC-3 tracks, as it gives an incorrect timestamp.
audioTimestampSet
=
!
isAc3
&&
audioTrackUtil
.
updateTimestamp
();
if
(
audioTimestampSet
)
{
// Perform sanity checks on the timestamp.
long
audioTimestampUs
=
audioTrackUtil
.
getTimestampNanoTime
()
/
1000
;
...
...
@@ -739,7 +751,7 @@ public final class AudioTrack {
private
static
class
AudioTrackUtil
{
protected
android
.
media
.
AudioTrack
audioTrack
;
private
boolean
enablePassthroughWorkaround
;
private
boolean
isPassthrough
;
private
int
sampleRate
;
private
long
lastRawPlaybackHeadPosition
;
private
long
rawPlaybackHeadWrapCount
;
...
...
@@ -749,14 +761,11 @@ public final class AudioTrack {
* Reconfigures the audio track utility helper to use the specified {@code audioTrack}.
*
* @param audioTrack The audio track to wrap.
* @param enablePassthroughWorkaround Whether to work around an issue where the playback head
* position jumps back to zero on a paused passthrough/direct audio track. See
* [Internal: b/19187573].
* @param isPassthrough Whether the audio track is used for passthrough (e.g. AC-3) playback.
*/
public
void
reconfigure
(
android
.
media
.
AudioTrack
audioTrack
,
boolean
enablePassthroughWorkaround
)
{
public
void
reconfigure
(
android
.
media
.
AudioTrack
audioTrack
,
boolean
isPassthrough
)
{
this
.
audioTrack
=
audioTrack
;
this
.
enablePassthroughWorkaround
=
enablePassthroughWorkaround
;
this
.
isPassthrough
=
isPassthrough
;
lastRawPlaybackHeadPosition
=
0
;
rawPlaybackHeadWrapCount
=
0
;
passthroughWorkaroundPauseOffset
=
0
;
...
...
@@ -767,14 +776,14 @@ public final class AudioTrack {
/**
* Returns whether the audio track should behave as though it has pending data. This is to work
* around an issue
where AC-3 audio tracks can't be paused, so we empty their buffers when
*
paused. In this case, they should still behave as if they have pending data, otherwis
e
* writing will never resume.
* around an issue
on platform API versions 21/22 where AC-3 audio tracks can't be paused, so we
*
empty their buffers when paused. In this case, they should still behave as if they hav
e
*
pending data, otherwise
writing will never resume.
*
* @see #handleBuffer
*/
public
boolean
overrideHasPendingData
()
{
return
enablePassthroughWorkaround
return
Util
.
SDK_INT
<=
22
&&
isPassthrough
&&
audioTrack
.
getPlayState
()
==
android
.
media
.
AudioTrack
.
PLAYSTATE_PAUSED
&&
audioTrack
.
getPlaybackHeadPosition
()
==
0
;
}
...
...
@@ -790,8 +799,16 @@ public final class AudioTrack {
*/
public
long
getPlaybackHeadPosition
()
{
long
rawPlaybackHeadPosition
=
0xFFFFFFFF
L
&
audioTrack
.
getPlaybackHeadPosition
();
if
(
enablePassthroughWorkaround
)
{
if
(
audioTrack
.
getPlayState
()
==
android
.
media
.
AudioTrack
.
PLAYSTATE_PAUSED
if
(
Util
.
SDK_INT
<=
22
&&
isPassthrough
)
{
// Work around issues with passthrough/direct AudioTracks on platform API versions 21/22:
// - After resetting, the new AudioTrack's playback position continues to increase for a
// short time from the old AudioTrack's position, while in the PLAYSTATE_STOPPED state.
// - The playback head position jumps back to zero on paused passthrough/direct audio
// tracks. See [Internal: b/19187573].
if
(
audioTrack
.
getPlayState
()
==
android
.
media
.
AudioTrack
.
PLAYSTATE_STOPPED
)
{
// Prevent detecting a wrapped position.
lastRawPlaybackHeadPosition
=
rawPlaybackHeadPosition
;
}
else
if
(
audioTrack
.
getPlayState
()
==
android
.
media
.
AudioTrack
.
PLAYSTATE_PAUSED
&&
rawPlaybackHeadPosition
==
0
)
{
passthroughWorkaroundPauseOffset
=
lastRawPlaybackHeadPosition
;
}
...
...
@@ -868,9 +885,8 @@ public final class AudioTrack {
}
@Override
public
void
reconfigure
(
android
.
media
.
AudioTrack
audioTrack
,
boolean
enablePassthroughWorkaround
)
{
super
.
reconfigure
(
audioTrack
,
enablePassthroughWorkaround
);
public
void
reconfigure
(
android
.
media
.
AudioTrack
audioTrack
,
boolean
isPassthrough
)
{
super
.
reconfigure
(
audioTrack
,
isPassthrough
);
rawTimestampFramePositionWrapCount
=
0
;
lastRawTimestampFramePosition
=
0
;
lastTimestampFramePosition
=
0
;
...
...
library/src/main/java/com/google/android/exoplayer/chunk/ChunkSampleSource.java
View file @
dd41d14a
...
...
@@ -87,7 +87,7 @@ public class ChunkSampleSource implements SampleSource, Loader.Callback {
private
long
currentLoadStartTimeMs
;
private
MediaFormat
downstreamMediaFormat
;
private
volatile
Format
downstreamFormat
;
private
Format
downstreamFormat
;
public
ChunkSampleSource
(
ChunkSource
chunkSource
,
LoadControl
loadControl
,
int
bufferSizeContribution
,
boolean
frameAccurateSeeking
)
{
...
...
@@ -120,18 +120,8 @@ public class ChunkSampleSource implements SampleSource, Loader.Callback {
pendingResetPositionUs
=
NO_RESET_PENDING
;
}
/**
* Exposes the current downstream format for debugging purposes. Can be called from any thread.
*
* @return The current downstream format.
*/
@Deprecated
public
Format
getFormat
()
{
return
downstreamFormat
;
}
@Override
public
boolean
prepare
()
{
public
boolean
prepare
(
long
positionUs
)
{
Assertions
.
checkState
(
state
==
STATE_UNPREPARED
);
loader
=
new
Loader
(
"Loader:"
+
chunkSource
.
getTrackInfo
().
mimeType
);
state
=
STATE_PREPARED
;
...
...
library/src/main/java/com/google/android/exoplayer/chunk/FormatEvaluator.java
View file @
dd41d14a
...
...
@@ -84,7 +84,7 @@ public interface FormatEvaluator {
/**
* Always selects the first format.
*/
public
static
class
FixedEvaluator
implements
FormatEvaluator
{
public
static
final
class
FixedEvaluator
implements
FormatEvaluator
{
@Override
public
void
enable
()
{
...
...
@@ -107,7 +107,7 @@ public interface FormatEvaluator {
/**
* Selects randomly between the available formats.
*/
public
static
class
RandomEvaluator
implements
FormatEvaluator
{
public
static
final
class
RandomEvaluator
implements
FormatEvaluator
{
private
final
Random
random
;
...
...
@@ -145,7 +145,7 @@ public interface FormatEvaluator {
* reference implementation only. It is recommended that application developers implement their
* own adaptive evaluator to more precisely suit their use case.
*/
public
static
class
AdaptiveEvaluator
implements
FormatEvaluator
{
public
static
final
class
AdaptiveEvaluator
implements
FormatEvaluator
{
public
static
final
int
DEFAULT_MAX_INITIAL_BITRATE
=
800000
;
...
...
@@ -259,8 +259,9 @@ public interface FormatEvaluator {
/**
* Compute the ideal format ignoring buffer health.
*/
protected
Format
determineIdealFormat
(
Format
[]
formats
,
long
bitrateEstimate
)
{
long
effectiveBitrate
=
computeEffectiveBitrateEstimate
(
bitrateEstimate
);
private
Format
determineIdealFormat
(
Format
[]
formats
,
long
bitrateEstimate
)
{
long
effectiveBitrate
=
bitrateEstimate
==
BandwidthMeter
.
NO_ESTIMATE
?
maxInitialBitrate
:
(
long
)
(
bitrateEstimate
*
bandwidthFraction
);
for
(
int
i
=
0
;
i
<
formats
.
length
;
i
++)
{
Format
format
=
formats
[
i
];
if
(
format
.
bitrate
<=
effectiveBitrate
)
{
...
...
@@ -271,14 +272,6 @@ public interface FormatEvaluator {
return
formats
[
formats
.
length
-
1
];
}
/**
* Apply overhead factor, or default value in absence of estimate.
*/
protected
long
computeEffectiveBitrateEstimate
(
long
bitrateEstimate
)
{
return
bitrateEstimate
==
BandwidthMeter
.
NO_ESTIMATE
?
maxInitialBitrate
:
(
long
)
(
bitrateEstimate
*
bandwidthFraction
);
}
}
}
library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java
View file @
dd41d14a
...
...
@@ -419,7 +419,9 @@ public class DashChunkSource implements ChunkSource {
(
ChunkIndex
)
initializationChunk
.
getSeekMap
(),
initializationChunk
.
dataSpec
.
uri
.
toString
());
}
if
(
initializationChunk
.
hasDrmInitData
())
{
// The null check avoids overwriting drmInitData obtained from the manifest with drmInitData
// obtained from the stream, as per DASH IF Interoperability Recommendations V3.0, 7.5.3.
if
(
drmInitData
==
null
&&
initializationChunk
.
hasDrmInitData
())
{
drmInitData
=
initializationChunk
.
getDrmInitData
();
}
}
...
...
library/src/main/java/com/google/android/exoplayer/dash/mpd/MediaPresentationDescriptionParser.java
View file @
dd41d14a
...
...
@@ -24,10 +24,12 @@ import com.google.android.exoplayer.dash.mpd.SegmentBase.SingleSegmentBase;
import
com.google.android.exoplayer.upstream.UriLoadable
;
import
com.google.android.exoplayer.util.Assertions
;
import
com.google.android.exoplayer.util.MimeTypes
;
import
com.google.android.exoplayer.util.ParsableByteArray
;
import
com.google.android.exoplayer.util.UriUtil
;
import
com.google.android.exoplayer.util.Util
;
import
android.text.TextUtils
;
import
android.util.Base64
;
import
org.xml.sax.helpers.DefaultHandler
;
import
org.xmlpull.v1.XmlPullParser
;
...
...
@@ -41,6 +43,7 @@ import java.util.ArrayList;
import
java.util.Collections
;
import
java.util.Comparator
;
import
java.util.List
;
import
java.util.UUID
;
import
java.util.regex.Matcher
;
import
java.util.regex.Pattern
;
...
...
@@ -270,11 +273,27 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
protected
ContentProtection
parseContentProtection
(
XmlPullParser
xpp
)
throws
XmlPullParserException
,
IOException
{
String
schemeIdUri
=
xpp
.
getAttributeValue
(
null
,
"schemeIdUri"
);
return
buildContentProtection
(
schemeIdUri
);
UUID
uuid
=
null
;
byte
[]
data
=
null
;
do
{
xpp
.
next
();
// The cenc:pssh element is defined in 23001-7:2015
if
(
isStartTag
(
xpp
,
"cenc:pssh"
)
&&
xpp
.
next
()
==
XmlPullParser
.
TEXT
)
{
byte
[]
decodedData
=
Base64
.
decode
(
xpp
.
getText
(),
Base64
.
DEFAULT
);
ParsableByteArray
psshAtom
=
new
ParsableByteArray
(
decodedData
);
psshAtom
.
skipBytes
(
12
);
uuid
=
new
UUID
(
psshAtom
.
readLong
(),
psshAtom
.
readLong
());
int
dataSize
=
psshAtom
.
readInt
();
data
=
new
byte
[
dataSize
];
psshAtom
.
readBytes
(
data
,
0
,
dataSize
);
}
}
while
(!
isEndTag
(
xpp
,
"ContentProtection"
));
return
buildContentProtection
(
schemeIdUri
,
uuid
,
data
);
}
protected
ContentProtection
buildContentProtection
(
String
schemeIdUri
)
{
return
new
ContentProtection
(
schemeIdUri
,
null
,
null
);
protected
ContentProtection
buildContentProtection
(
String
schemeIdUri
,
UUID
uuid
,
byte
[]
data
)
{
return
new
ContentProtection
(
schemeIdUri
,
uuid
,
data
);
}
/**
...
...
library/src/main/java/com/google/android/exoplayer/extractor/ExtractorSampleSource.java
View file @
dd41d14a
...
...
@@ -72,6 +72,7 @@ public class ExtractorSampleSource implements SampleSource, ExtractorOutput, Loa
private
boolean
prepared
;
private
int
enabledTrackCount
;
private
TrackInfo
[]
trackInfos
;
private
long
maxTrackDurationUs
;
private
boolean
[]
pendingMediaFormat
;
private
boolean
[]
pendingDiscontinuities
;
private
boolean
[]
trackEnabledStates
;
...
...
@@ -137,7 +138,7 @@ public class ExtractorSampleSource implements SampleSource, ExtractorOutput, Loa
}
@Override
public
boolean
prepare
()
throws
IOException
{
public
boolean
prepare
(
long
positionUs
)
throws
IOException
{
if
(
prepared
)
{
return
true
;
}
...
...
@@ -156,9 +157,13 @@ public class ExtractorSampleSource implements SampleSource, ExtractorOutput, Loa
pendingDiscontinuities
=
new
boolean
[
trackCount
];
pendingMediaFormat
=
new
boolean
[
trackCount
];
trackInfos
=
new
TrackInfo
[
trackCount
];
maxTrackDurationUs
=
C
.
UNKNOWN_TIME_US
;
for
(
int
i
=
0
;
i
<
trackCount
;
i
++)
{
MediaFormat
format
=
sampleQueues
.
valueAt
(
i
).
getFormat
();
trackInfos
[
i
]
=
new
TrackInfo
(
format
.
mimeType
,
format
.
durationUs
);
if
(
format
.
durationUs
!=
C
.
UNKNOWN_TIME_US
&&
format
.
durationUs
>
maxTrackDurationUs
)
{
maxTrackDurationUs
=
format
.
durationUs
;
}
}
prepared
=
true
;
return
true
;
...
...
@@ -448,6 +453,11 @@ public class ExtractorSampleSource implements SampleSource, ExtractorOutput, Loa
loadable
=
createLoadableFromStart
();
}
else
{
Assertions
.
checkState
(
isPendingReset
());
if
(
maxTrackDurationUs
!=
C
.
UNKNOWN_TIME_US
&&
pendingResetPositionUs
>=
maxTrackDurationUs
)
{
loadingFinished
=
true
;
pendingResetPositionUs
=
NO_RESET_PENDING
;
return
;
}
loadable
=
createLoadableFromPositionUs
(
pendingResetPositionUs
);
pendingResetPositionUs
=
NO_RESET_PENDING
;
}
...
...
library/src/main/java/com/google/android/exoplayer/extractor/mp3/XingSeeker.java
View file @
dd41d14a
...
...
@@ -137,7 +137,7 @@ import com.google.android.exoplayer.util.Util;
}
long
position
=
(
long
)
((
1
f
/
256
)
*
fx
*
sizeBytes
)
+
firstFramePosition
;
return
inputLength
!=
C
.
LENGTH_UNBOUNDED
?
Math
.
min
(
position
,
inputLength
)
:
position
;
return
inputLength
!=
C
.
LENGTH_UNBOUNDED
?
Math
.
min
(
position
,
inputLength
-
1
)
:
position
;
}
@Override
...
...
library/src/main/java/com/google/android/exoplayer/extractor/mp4/AtomParsers.java
View file @
dd41d14a
...
...
@@ -534,8 +534,18 @@ import java.util.List;
childPosition
+=
childAtomSize
;
}
out
.
mediaFormat
=
MediaFormat
.
createAudioFormat
(
MimeTypes
.
AUDIO_AAC
,
sampleSize
,
durationUs
,
channelCount
,
sampleRate
,
// Set the MIME type for ac-3/ec-3 atoms even if the dac3/dec3 child atom is missing.
String
mimeType
;
if
(
atomType
==
Atom
.
TYPE_ac_3
)
{
mimeType
=
MimeTypes
.
AUDIO_AC3
;
}
else
if
(
atomType
==
Atom
.
TYPE_ec_3
)
{
mimeType
=
MimeTypes
.
AUDIO_EC3
;
}
else
{
mimeType
=
MimeTypes
.
AUDIO_AAC
;
}
out
.
mediaFormat
=
MediaFormat
.
createAudioFormat
(
mimeType
,
sampleSize
,
durationUs
,
channelCount
,
sampleRate
,
initializationData
==
null
?
null
:
Collections
.
singletonList
(
initializationData
));
}
...
...
library/src/main/java/com/google/android/exoplayer/extractor/ts/TsExtractor.java
View file @
dd41d14a
...
...
@@ -16,6 +16,7 @@
package
com
.
google
.
android
.
exoplayer
.
extractor
.
ts
;
import
com.google.android.exoplayer.C
;
import
com.google.android.exoplayer.audio.AudioCapabilities
;
import
com.google.android.exoplayer.extractor.Extractor
;
import
com.google.android.exoplayer.extractor.ExtractorInput
;
import
com.google.android.exoplayer.extractor.ExtractorOutput
;
...
...
@@ -43,7 +44,7 @@ public final class TsExtractor implements Extractor, SeekMap {
private
static
final
int
TS_STREAM_TYPE_AAC
=
0x0F
;
private
static
final
int
TS_STREAM_TYPE_ATSC_AC3
=
0x81
;
private
static
final
int
TS_STREAM_TYPE_
DVB_AC3
=
0x06
;
private
static
final
int
TS_STREAM_TYPE_
ATSC_E_AC3
=
0x87
;
private
static
final
int
TS_STREAM_TYPE_H264
=
0x1B
;
private
static
final
int
TS_STREAM_TYPE_ID3
=
0x15
;
private
static
final
int
TS_STREAM_TYPE_EIA608
=
0x100
;
// 0xFF + 1
...
...
@@ -52,6 +53,7 @@ public final class TsExtractor implements Extractor, SeekMap {
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
;
...
...
@@ -62,14 +64,15 @@ public final class TsExtractor implements Extractor, SeekMap {
private
long
lastPts
;
public
TsExtractor
()
{
this
(
0
);
this
(
0
,
null
);
}
public
TsExtractor
(
long
firstSampleTimestampUs
)
{
public
TsExtractor
(
long
firstSampleTimestampUs
,
AudioCapabilities
audioCapabilities
)
{
this
.
firstSampleTimestampUs
=
firstSampleTimestampUs
;
tsScratch
=
new
ParsableBitArray
(
new
byte
[
3
]);
tsPacketBuffer
=
new
ParsableByteArray
(
TS_PACKET_SIZE
);
streamTypes
=
new
SparseBooleanArray
();
allowedPassthroughStreamTypes
=
getPassthroughStreamTypes
(
audioCapabilities
);
tsPayloadReaders
=
new
SparseArray
<
TsPayloadReader
>();
tsPayloadReaders
.
put
(
TS_PAT_PID
,
new
PatReader
());
lastPts
=
Long
.
MIN_VALUE
;
...
...
@@ -175,6 +178,24 @@ public final class TsExtractor implements Extractor, SeekMap {
}
/**
* Returns a sparse boolean array of stream types that can be played back based on
* {@code audioCapabilities}.
*/
private
static
SparseBooleanArray
getPassthroughStreamTypes
(
AudioCapabilities
audioCapabilities
)
{
SparseBooleanArray
streamTypes
=
new
SparseBooleanArray
();
if
(
audioCapabilities
!=
null
)
{
if
(
audioCapabilities
.
supportsEncoding
(
C
.
ENCODING_AC3
))
{
streamTypes
.
put
(
TS_STREAM_TYPE_ATSC_AC3
,
true
);
}
if
(
audioCapabilities
.
supportsEncoding
(
C
.
ENCODING_E_AC3
))
{
// TODO: Uncomment when Ac3Reader supports enhanced AC-3.
// streamTypes.put(TS_STREAM_TYPE_ATSC_E_AC3, true);
}
}
return
streamTypes
;
}
/**
* Parses TS packet payload data.
*/
private
abstract
static
class
TsPayloadReader
{
...
...
@@ -308,13 +329,17 @@ public final class TsExtractor implements Extractor, SeekMap {
continue
;
}
// TODO: Detect and read DVB AC-3 streams with Ac3Reader.
ElementaryStreamReader
pesPayloadReader
=
null
;
switch
(
streamType
)
{
case
TS_STREAM_TYPE_AAC:
pesPayloadReader
=
new
AdtsReader
(
output
.
track
(
TS_STREAM_TYPE_AAC
));
break
;
case
TS_STREAM_TYPE_ATSC_E_AC3:
case
TS_STREAM_TYPE_ATSC_AC3:
case
TS_STREAM_TYPE_DVB_AC3:
if
(!
allowedPassthroughStreamTypes
.
get
(
streamType
))
{
continue
;
}
pesPayloadReader
=
new
Ac3Reader
(
output
.
track
(
streamType
));
break
;
case
TS_STREAM_TYPE_H264:
...
...
library/src/main/java/com/google/android/exoplayer/hls/HlsChunkSource.java
View file @
dd41d14a
...
...
@@ -17,6 +17,7 @@ package com.google.android.exoplayer.hls;
import
com.google.android.exoplayer.C
;
import
com.google.android.exoplayer.MediaFormat
;
import
com.google.android.exoplayer.audio.AudioCapabilities
;
import
com.google.android.exoplayer.chunk.BaseChunkSampleSourceEventListener
;
import
com.google.android.exoplayer.chunk.Chunk
;
import
com.google.android.exoplayer.chunk.DataChunk
;
...
...
@@ -125,6 +126,7 @@ public class HlsChunkSource {
private
final
int
maxHeight
;
private
final
long
minBufferDurationToSwitchUpUs
;
private
final
long
maxBufferDurationToSwitchDownUs
;
private
final
AudioCapabilities
audioCapabilities
;
/* package */
byte
[]
scratchSpace
;
/* package */
final
HlsMediaPlaylist
[]
mediaPlaylists
;
...
...
@@ -140,9 +142,11 @@ public class HlsChunkSource {
private
byte
[]
encryptionIv
;
public
HlsChunkSource
(
DataSource
dataSource
,
String
playlistUrl
,
HlsPlaylist
playlist
,
BandwidthMeter
bandwidthMeter
,
int
[]
variantIndices
,
int
adaptiveMode
)
{
BandwidthMeter
bandwidthMeter
,
int
[]
variantIndices
,
int
adaptiveMode
,
AudioCapabilities
audioCapabilities
)
{
this
(
dataSource
,
playlistUrl
,
playlist
,
bandwidthMeter
,
variantIndices
,
adaptiveMode
,
DEFAULT_MIN_BUFFER_TO_SWITCH_UP_MS
,
DEFAULT_MAX_BUFFER_TO_SWITCH_DOWN_MS
);
DEFAULT_MIN_BUFFER_TO_SWITCH_UP_MS
,
DEFAULT_MAX_BUFFER_TO_SWITCH_DOWN_MS
,
audioCapabilities
);
}
/**
...
...
@@ -160,13 +164,17 @@ public class HlsChunkSource {
* for a switch to a higher quality variant to be considered.
* @param maxBufferDurationToSwitchDownMs The maximum duration of media that needs to be buffered
* for a switch to a lower quality variant to be considered.
* @param audioCapabilities The audio capabilities for playback on this device, or {@code null} if
* the default capabilities should be assumed.
*/
public
HlsChunkSource
(
DataSource
dataSource
,
String
playlistUrl
,
HlsPlaylist
playlist
,
BandwidthMeter
bandwidthMeter
,
int
[]
variantIndices
,
int
adaptiveMode
,
long
minBufferDurationToSwitchUpMs
,
long
maxBufferDurationToSwitchDownMs
)
{
long
minBufferDurationToSwitchUpMs
,
long
maxBufferDurationToSwitchDownMs
,
AudioCapabilities
audioCapabilities
)
{
this
.
dataSource
=
dataSource
;
this
.
bandwidthMeter
=
bandwidthMeter
;
this
.
adaptiveMode
=
adaptiveMode
;
this
.
audioCapabilities
=
audioCapabilities
;
minBufferDurationToSwitchUpUs
=
minBufferDurationToSwitchUpMs
*
1000
;
maxBufferDurationToSwitchDownUs
=
maxBufferDurationToSwitchDownMs
*
1000
;
baseUri
=
playlist
.
baseUri
;
...
...
@@ -235,16 +243,14 @@ public class HlsChunkSource {
public
Chunk
getChunkOperation
(
TsChunk
previousTsChunk
,
long
seekPositionUs
,
long
playbackPositionUs
)
{
int
nextFormatIndex
;
boolean
switchingVariant
;
boolean
switchingVariantSpliced
;
if
(
adaptiveMode
==
ADAPTIVE_MODE_NONE
)
{
nextFormatIndex
=
formatIndex
;
switchingVariant
=
false
;
switchingVariantSpliced
=
false
;
}
else
{
nextFormatIndex
=
getNextFormatIndex
(
previousTsChunk
,
playbackPositionUs
);
switchingVariant
=
nextFormatIndex
!=
formatIndex
;
switchingVariantSpliced
=
switchingVariant
&&
adaptiveMode
==
ADAPTIVE_MODE_SPLICE
;
switchingVariant
Spliced
=
nextFormatIndex
!=
formatIndex
&&
adaptiveMode
==
ADAPTIVE_MODE_SPLICE
;
}
int
variantIndex
=
getVariantIndex
(
enabledFormats
[
nextFormatIndex
]);
...
...
@@ -331,10 +337,11 @@ public class HlsChunkSource {
// Configure the extractor that will read the chunk.
HlsExtractorWrapper
extractorWrapper
;
if
(
previousTsChunk
==
null
||
segment
.
discontinuity
||
switchingVariant
||
liveDiscontinuity
)
{
if
(
previousTsChunk
==
null
||
segment
.
discontinuity
||
!
format
.
equals
(
previousTsChunk
.
format
)
||
liveDiscontinuity
)
{
Extractor
extractor
=
chunkUri
.
getLastPathSegment
().
endsWith
(
AAC_FILE_EXTENSION
)
?
new
AdtsExtractor
(
startTimeUs
)
:
new
TsExtractor
(
startTimeUs
);
:
new
TsExtractor
(
startTimeUs
,
audioCapabilities
);
extractorWrapper
=
new
HlsExtractorWrapper
(
trigger
,
format
,
startTimeUs
,
extractor
,
switchingVariantSpliced
);
}
else
{
...
...
library/src/main/java/com/google/android/exoplayer/hls/HlsSampleSource.java
View file @
dd41d14a
...
...
@@ -125,15 +125,12 @@ public class HlsSampleSource implements SampleSource, Loader.Callback {
}
@Override
public
boolean
prepare
()
throws
IOException
{
public
boolean
prepare
(
long
positionUs
)
throws
IOException
{
if
(
prepared
)
{
return
true
;
}
if
(
loader
==
null
)
{
loader
=
new
Loader
(
"Loader:HLS"
);
}
continueBufferingInternal
();
if
(!
extractors
.
isEmpty
())
{
// We're not prepared, but we might have loaded what we need.
HlsExtractorWrapper
extractor
=
extractors
.
getFirst
();
if
(
extractor
.
isPrepared
())
{
trackCount
=
extractor
.
getTrackCount
();
...
...
@@ -146,12 +143,23 @@ public class HlsSampleSource implements SampleSource, Loader.Callback {
trackInfos
[
i
]
=
new
TrackInfo
(
format
.
mimeType
,
chunkSource
.
getDurationUs
());
}
prepared
=
true
;
return
true
;
}
}
if
(!
prepared
)
{
maybeThrowLoadableException
();
// We're not prepared and we haven't loaded what we need.
if
(
loader
==
null
)
{
loader
=
new
Loader
(
"Loader:HLS"
);
}
return
prepared
;
if
(!
loader
.
isLoading
())
{
// We're going to have to start loading a chunk to get what we need for preparation. We should
// attempt to load the chunk at positionUs, so that we'll already be loading the correct chunk
// in the common case where the renderer is subsequently enabled at this position.
pendingResetPositionUs
=
positionUs
;
downstreamPositionUs
=
positionUs
;
}
maybeStartLoading
();
maybeThrowLoadableException
();
return
false
;
}
@Override
...
...
@@ -345,7 +353,12 @@ public class HlsSampleSource implements SampleSource, Loader.Callback {
if
(!
currentLoadableExceptionFatal
)
{
clearCurrentLoadable
();
}
maybeStartLoading
();
if
(
enabledTrackCount
>
0
)
{
maybeStartLoading
();
}
else
{
clearState
();
allocator
.
trim
(
0
);
}
}
@Override
...
...
library/src/main/java/com/google/android/exoplayer/metadata/MetadataTrackRenderer.java
View file @
dd41d14a
...
...
@@ -90,9 +90,9 @@ public class MetadataTrackRenderer<T> extends TrackRenderer implements Callback
}
@Override
protected
int
doPrepare
()
throws
ExoPlaybackException
{
protected
int
doPrepare
(
long
positionUs
)
throws
ExoPlaybackException
{
try
{
boolean
sourcePrepared
=
source
.
prepare
();
boolean
sourcePrepared
=
source
.
prepare
(
positionUs
);
if
(!
sourcePrepared
)
{
return
TrackRenderer
.
STATE_UNPREPARED
;
}
...
...
library/src/main/java/com/google/android/exoplayer/text/TextTrackRenderer.java
View file @
dd41d14a
...
...
@@ -80,9 +80,9 @@ public class TextTrackRenderer extends TrackRenderer implements Callback {
}
@Override
protected
int
doPrepare
()
throws
ExoPlaybackException
{
protected
int
doPrepare
(
long
positionUs
)
throws
ExoPlaybackException
{
try
{
boolean
sourcePrepared
=
source
.
prepare
();
boolean
sourcePrepared
=
source
.
prepare
(
positionUs
);
if
(!
sourcePrepared
)
{
return
TrackRenderer
.
STATE_UNPREPARED
;
}
...
...
library/src/main/java/com/google/android/exoplayer/text/eia608/Eia608TrackRenderer.java
View file @
dd41d14a
...
...
@@ -90,9 +90,9 @@ public class Eia608TrackRenderer extends TrackRenderer implements Callback {
}
@Override
protected
int
doPrepare
()
throws
ExoPlaybackException
{
protected
int
doPrepare
(
long
positionUs
)
throws
ExoPlaybackException
{
try
{
boolean
sourcePrepared
=
source
.
prepare
();
boolean
sourcePrepared
=
source
.
prepare
(
positionUs
);
if
(!
sourcePrepared
)
{
return
TrackRenderer
.
STATE_UNPREPARED
;
}
...
...
library/src/main/java/com/google/android/exoplayer/upstream/BandwidthMeter.java
View file @
dd41d14a
...
...
@@ -18,7 +18,24 @@ package com.google.android.exoplayer.upstream;
/**
* Provides estimates of the currently available bandwidth.
*/
public
interface
BandwidthMeter
{
public
interface
BandwidthMeter
extends
TransferListener
{
/**
* Interface definition for a callback to be notified of {@link BandwidthMeter} events.
*/
public
interface
EventListener
{
/**
* Invoked periodically to indicate that bytes have been transferred.
*
* @param elapsedMs The time taken to transfer the bytes, in milliseconds.
* @param bytes The number of bytes transferred.
* @param bitrate The estimated bitrate in bits/sec, or {@link #NO_ESTIMATE} if no estimate
* is available. Note that this estimate is typically derived from more information than
* {@code bytes} and {@code elapsedMs}.
*/
void
onBandwidthSample
(
int
elapsedMs
,
long
bytes
,
long
bitrate
);
}
/**
* Indicates no bandwidth estimate is available.
...
...
library/src/main/java/com/google/android/exoplayer/upstream/DefaultBandwidthMeter.java
View file @
dd41d14a
...
...
@@ -26,25 +26,7 @@ import android.os.Handler;
* Counts transferred bytes while transfers are open and creates a bandwidth sample and updated
* bandwidth estimate each time a transfer ends.
*/
public
class
DefaultBandwidthMeter
implements
BandwidthMeter
,
TransferListener
{
/**
* Interface definition for a callback to be notified of {@link DefaultBandwidthMeter} events.
*/
public
interface
EventListener
{
/**
* Invoked periodically to indicate that bytes have been transferred.
*
* @param elapsedMs The time taken to transfer the bytes, in milliseconds.
* @param bytes The number of bytes transferred.
* @param bitrate The estimated bitrate in bits/sec, or {@link #NO_ESTIMATE} if no estimate
* is available. Note that this estimate is typically derived from more information than
* {@code bytes} and {@code elapsedMs}.
*/
void
onBandwidthSample
(
int
elapsedMs
,
long
bytes
,
long
bitrate
);
}
public
class
DefaultBandwidthMeter
implements
BandwidthMeter
{
private
static
final
int
DEFAULT_MAX_WEIGHT
=
2000
;
...
...
library/src/main/java/com/google/android/exoplayer/upstream/DefaultHttpDataSource.java
View file @
dd41d14a
...
...
@@ -28,6 +28,8 @@ import java.io.IOException;
import
java.io.InputStream
;
import
java.io.InterruptedIOException
;
import
java.net.HttpURLConnection
;
import
java.net.NoRouteToHostException
;
import
java.net.ProtocolException
;
import
java.net.URL
;
import
java.util.HashMap
;
import
java.util.List
;
...
...
@@ -38,17 +40,30 @@ import java.util.regex.Pattern;
/**
* A {@link HttpDataSource} that uses Android's {@link HttpURLConnection}.
* <p>
* By default this implementation will not follow cross-protocol redirects (i.e. redirects from
* HTTP to HTTPS or vice versa). Cross-protocol redirects can be enabled by using the
* {@link #DefaultHttpDataSource(String, Predicate, TransferListener, int, int, boolean)}
* constructor and passing {@code true} as the final argument.
*/
public
class
DefaultHttpDataSource
implements
HttpDataSource
{
/**
* The default connection timeout, in milliseconds.
*/
public
static
final
int
DEFAULT_CONNECT_TIMEOUT_MILLIS
=
8
*
1000
;
/**
* The default read timeout, in milliseconds.
*/
public
static
final
int
DEFAULT_READ_TIMEOUT_MILLIS
=
8
*
1000
;
private
static
final
int
MAX_REDIRECTS
=
20
;
// Same limit as okhttp.
private
static
final
String
TAG
=
"HttpDataSource"
;
private
static
final
Pattern
CONTENT_RANGE_HEADER
=
Pattern
.
compile
(
"^bytes (\\d+)-(\\d+)/(\\d+)$"
);
private
static
final
AtomicReference
<
byte
[]>
skipBufferReference
=
new
AtomicReference
<
byte
[]>();
private
final
boolean
allowCrossProtocolRedirects
;
private
final
int
connectTimeoutMillis
;
private
final
int
readTimeoutMillis
;
private
final
String
userAgent
;
...
...
@@ -103,12 +118,33 @@ public class DefaultHttpDataSource implements HttpDataSource {
*/
public
DefaultHttpDataSource
(
String
userAgent
,
Predicate
<
String
>
contentTypePredicate
,
TransferListener
listener
,
int
connectTimeoutMillis
,
int
readTimeoutMillis
)
{
this
(
userAgent
,
contentTypePredicate
,
listener
,
connectTimeoutMillis
,
readTimeoutMillis
,
false
);
}
/**
* @param userAgent The User-Agent string that should be used.
* @param contentTypePredicate An optional {@link Predicate}. If a content type is
* rejected by the predicate then a {@link HttpDataSource.InvalidContentTypeException} is
* thrown from {@link #open(DataSpec)}.
* @param listener An optional listener.
* @param connectTimeoutMillis The connection timeout, in milliseconds. A timeout of zero is
* interpreted as an infinite timeout. Pass {@link #DEFAULT_CONNECT_TIMEOUT_MILLIS} to use
* the default value.
* @param readTimeoutMillis The read timeout, in milliseconds. A timeout of zero is interpreted
* as an infinite timeout. Pass {@link #DEFAULT_READ_TIMEOUT_MILLIS} to use the default value.
* @param allowCrossProtocolRedirects Whether cross-protocol redirects (i.e. redirects from HTTP
* to HTTPS and vice versa) are enabled.
*/
public
DefaultHttpDataSource
(
String
userAgent
,
Predicate
<
String
>
contentTypePredicate
,
TransferListener
listener
,
int
connectTimeoutMillis
,
int
readTimeoutMillis
,
boolean
allowCrossProtocolRedirects
)
{
this
.
userAgent
=
Assertions
.
checkNotEmpty
(
userAgent
);
this
.
contentTypePredicate
=
contentTypePredicate
;
this
.
listener
=
listener
;
this
.
requestProperties
=
new
HashMap
<
String
,
String
>();
this
.
connectTimeoutMillis
=
connectTimeoutMillis
;
this
.
readTimeoutMillis
=
readTimeoutMillis
;
this
.
allowCrossProtocolRedirects
=
allowCrossProtocolRedirects
;
}
@Override
...
...
@@ -283,8 +319,58 @@ public class DefaultHttpDataSource implements HttpDataSource {
return
bytesToRead
==
C
.
LENGTH_UNBOUNDED
?
bytesToRead
:
bytesToRead
-
bytesRead
;
}
/**
* Establishes a connection, following redirects to do so where permitted.
*/
private
HttpURLConnection
makeConnection
(
DataSpec
dataSpec
)
throws
IOException
{
URL
url
=
new
URL
(
dataSpec
.
uri
.
toString
());
long
position
=
dataSpec
.
position
;
long
length
=
dataSpec
.
length
;
boolean
allowGzip
=
(
dataSpec
.
flags
&
DataSpec
.
FLAG_ALLOW_GZIP
)
!=
0
;
if
(!
allowCrossProtocolRedirects
)
{
// HttpURLConnection disallows cross-protocol redirects, but otherwise performs redirection
// automatically. This is the behavior we want, so use it.
HttpURLConnection
connection
=
configureConnection
(
url
,
position
,
length
,
allowGzip
);
connection
.
connect
();
return
connection
;
}
// We need to handle redirects ourselves to allow cross-protocol redirects.
int
redirectCount
=
0
;
while
(
redirectCount
++
<=
MAX_REDIRECTS
)
{
HttpURLConnection
connection
=
configureConnection
(
url
,
position
,
length
,
allowGzip
);
connection
.
setInstanceFollowRedirects
(
false
);
connection
.
connect
();
int
responseCode
=
connection
.
getResponseCode
();
if
(
responseCode
==
HttpURLConnection
.
HTTP_MULT_CHOICE
||
responseCode
==
HttpURLConnection
.
HTTP_MOVED_PERM
||
responseCode
==
HttpURLConnection
.
HTTP_MOVED_TEMP
||
responseCode
==
HttpURLConnection
.
HTTP_SEE_OTHER
||
responseCode
==
307
/* HTTP_TEMP_REDIRECT */
||
responseCode
==
308
/* HTTP_PERM_REDIRECT */
)
{
String
location
=
connection
.
getHeaderField
(
"Location"
);
connection
.
disconnect
();
url
=
handleRedirect
(
url
,
location
);
}
else
{
return
connection
;
}
}
// If we get here we've been redirected more times than are permitted.
throw
new
NoRouteToHostException
(
"Too many redirects: "
+
redirectCount
);
}
/**
* Configures a connection, but does not open it.
*
* @param url The url to connect to.
* @param position The byte offset of the requested data.
* @param length The length of the requested data, or {@link C#LENGTH_UNBOUNDED}.
* @param allowGzip Whether to allow the use of gzip.
*/
private
HttpURLConnection
configureConnection
(
URL
url
,
long
position
,
long
length
,
boolean
allowGzip
)
throws
IOException
{
HttpURLConnection
connection
=
(
HttpURLConnection
)
url
.
openConnection
();
connection
.
setConnectTimeout
(
connectTimeoutMillis
);
connection
.
setReadTimeout
(
readTimeoutMillis
);
...
...
@@ -294,28 +380,56 @@ public class DefaultHttpDataSource implements HttpDataSource {
connection
.
setRequestProperty
(
property
.
getKey
(),
property
.
getValue
());
}
}
setRangeHeader
(
connection
,
dataSpec
);
if
(!(
position
==
0
&&
length
==
C
.
LENGTH_UNBOUNDED
))
{
String
rangeRequest
=
"bytes="
+
position
+
"-"
;
if
(
length
!=
C
.
LENGTH_UNBOUNDED
)
{
rangeRequest
+=
(
position
+
length
-
1
);
}
connection
.
setRequestProperty
(
"Range"
,
rangeRequest
);
}
connection
.
setRequestProperty
(
"User-Agent"
,
userAgent
);
if
(
(
dataSpec
.
flags
&
DataSpec
.
FLAG_ALLOW_GZIP
)
==
0
)
{
if
(
!
allowGzip
)
{
connection
.
setRequestProperty
(
"Accept-Encoding"
,
"identity"
);
}
connection
.
connect
();
return
connection
;
}
private
void
setRangeHeader
(
HttpURLConnection
connection
,
DataSpec
dataSpec
)
{
if
(
dataSpec
.
position
==
0
&&
dataSpec
.
length
==
C
.
LENGTH_UNBOUNDED
)
{
// Not required.
return
;
/**
* Handles a redirect.
*
* @param originalUrl The original URL.
* @param location The Location header in the response.
* @return The next URL.
* @throws IOException If redirection isn't possible.
*/
private
static
URL
handleRedirect
(
URL
originalUrl
,
String
location
)
throws
IOException
{
if
(
location
==
null
)
{
throw
new
ProtocolException
(
"Null location redirect"
);
}
String
rangeRequest
=
"bytes="
+
dataSpec
.
position
+
"-"
;
if
(
dataSpec
.
length
!=
C
.
LENGTH_UNBOUNDED
)
{
rangeRequest
+=
(
dataSpec
.
position
+
dataSpec
.
length
-
1
);
// Form the new url.
URL
url
=
new
URL
(
originalUrl
,
location
);
// Check that the protocol of the new url is supported.
String
protocol
=
url
.
getProtocol
();
if
(!
"https"
.
equals
(
protocol
)
&&
!
"http"
.
equals
(
protocol
))
{
throw
new
ProtocolException
(
"Unsupported protocol redirect: "
+
protocol
);
}
connection
.
setRequestProperty
(
"Range"
,
rangeRequest
);
// Currently this method is only called if allowCrossProtocolRedirects is true, and so the code
// below isn't required. If we ever decide to handle redirects ourselves when cross-protocol
// redirects are disabled, we'll need to uncomment this block of code.
// if (!allowCrossProtocolRedirects && !protocol.equals(originalUrl.getProtocol())) {
// throw new ProtocolException("Disallowed cross-protocol redirect ("
// + originalUrl.getProtocol() + " to " + protocol + ")");
// }
return
url
;
}
private
long
getContentLength
(
HttpURLConnection
connection
)
{
/**
* Attempts to extract the length of the content from the response headers of an open connection.
*
* @param connection The open connection.
* @return The extracted length, or {@link C#LENGTH_UNBOUNDED}.
*/
private
static
long
getContentLength
(
HttpURLConnection
connection
)
{
long
contentLength
=
C
.
LENGTH_UNBOUNDED
;
String
contentLengthHeader
=
connection
.
getHeaderField
(
"Content-Length"
);
if
(!
TextUtils
.
isEmpty
(
contentLengthHeader
))
{
...
...
@@ -429,6 +543,9 @@ public class DefaultHttpDataSource implements HttpDataSource {
return
read
;
}
/**
* Closes the current connection, if there is one.
*/
private
void
closeConnection
()
{
if
(
connection
!=
null
)
{
connection
.
disconnect
();
...
...
library/src/main/java/com/google/android/exoplayer/upstream/DefaultUriDataSource.java
View file @
dd41d14a
...
...
@@ -36,15 +36,36 @@ 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
n
* Constructs a new data source that delegates to a {@link FileDataSource} for file URIs and a
* {@link DefaultHttpDataSource} for other URIs.
* <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.
*
* @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
);
}
/**
* Constructs a new data source that delegates to a {@link FileDataSource} for file URIs and a
* {@link DefaultHttpDataSource} for other URIs.
*
* @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
,
boolean
allowCrossProtocolRedirects
)
{
this
(
new
FileDataSource
(
transferListener
),
new
DefaultHttpDataSource
(
userAgent
,
null
,
transferListener
));
new
DefaultHttpDataSource
(
userAgent
,
null
,
transferListener
,
DefaultHttpDataSource
.
DEFAULT_CONNECT_TIMEOUT_MILLIS
,
DefaultHttpDataSource
.
DEFAULT_READ_TIMEOUT_MILLIS
,
allowCrossProtocolRedirects
));
}
/**
...
...
library/src/main/java/com/google/android/exoplayer/util/MimeTypes.java
View file @
dd41d14a
...
...
@@ -15,6 +15,11 @@
*/
package
com
.
google
.
android
.
exoplayer
.
util
;
import
com.google.android.exoplayer.C
;
import
com.google.android.exoplayer.audio.AudioCapabilities
;
import
android.media.AudioFormat
;
/**
* Defines common MIME types and helper methods.
*/
...
...
@@ -119,4 +124,37 @@ public class MimeTypes {
return
mimeType
.
equals
(
APPLICATION_TTML
);
}
/**
* Returns the output audio encoding that will result from processing input in {@code mimeType}.
* For non-passthrough audio formats, this is always {@link AudioFormat#ENCODING_PCM_16BIT}. For
* passthrough formats it will be one of {@link AudioFormat}'s other {@code ENCODING_*} constants.
* For non-audio formats, {@link AudioFormat#ENCODING_INVALID} will be returned.
*
* @param mimeType The MIME type of media that will be decoded (or passed through).
* @return The corresponding {@link AudioFormat} encoding.
*/
public
static
int
getEncodingForMimeType
(
String
mimeType
)
{
if
(
AUDIO_AC3
.
equals
(
mimeType
))
{
return
C
.
ENCODING_AC3
;
}
if
(
AUDIO_EC3
.
equals
(
mimeType
))
{
return
C
.
ENCODING_E_AC3
;
}
// All other audio formats will be decoded to 16-bit PCM.
return
isAudio
(
mimeType
)
?
AudioFormat
.
ENCODING_PCM_16BIT
:
AudioFormat
.
ENCODING_INVALID
;
}
/**
* Returns whether the specified {@code mimeType} represents audio that can be played via
* passthrough if the device supports it.
*
* @param mimeType The MIME type of input media.
* @return Whether the audio can be played via passthrough. If this method returns {@code true},
* it is still necessary to check the {@link AudioCapabilities} for device support.
*/
public
static
boolean
isPassthroughAudio
(
String
mimeType
)
{
return
AUDIO_AC3
.
equals
(
mimeType
)
||
AUDIO_EC3
.
equals
(
mimeType
);
}
}
library/src/main/project.properties
View file @
dd41d14a
...
...
@@ -8,5 +8,5 @@
# project structure.
# Project target.
target
=
android-2
1
target
=
android-2
2
android.library
=
true
library/src/test/AndroidManifest.xml
View file @
dd41d14a
...
...
@@ -17,7 +17,7 @@
<manifest
xmlns:android=
"http://schemas.android.com/apk/res/android"
package=
"com.google.android.exoplayer.tests"
>
<uses-sdk
android:minSdkVersion=
"9"
android:targetSdkVersion=
"2
1
"
/>
<uses-sdk
android:minSdkVersion=
"9"
android:targetSdkVersion=
"2
2
"
/>
<application>
<uses-library
android:name=
"android.test.runner"
/>
...
...
library/src/test/java/com/google/android/exoplayer/extractor/mp4/Mp4ExtractorTest.java
View file @
dd41d14a
...
...
@@ -579,7 +579,7 @@ public class Mp4ExtractorTest extends TestCase {
try
{
switch
(
message
.
what
)
{
case
MSG_PREPARE:
if
(!
source
.
prepare
())
{
if
(!
source
.
prepare
(
0
))
{
sendEmptyMessage
(
MSG_PREPARE
);
}
else
{
// Select the video track and get its metadata.
...
...
library/src/test/project.properties
View file @
dd41d14a
...
...
@@ -11,4 +11,4 @@
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
# Project target.
target
=
android-2
1
target
=
android-2
2
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