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 {
...
@@ -20,7 +20,7 @@ buildscript {
jcenter
()
jcenter
()
}
}
dependencies
{
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'
classpath
'com.novoda:bintray-release:0.2.7'
}
}
}
}
...
...
demo/build.gradle
View file @
dd41d14a
...
@@ -14,12 +14,12 @@
...
@@ -14,12 +14,12 @@
apply
plugin:
'com.android.application'
apply
plugin:
'com.android.application'
android
{
android
{
compileSdkVersion
2
1
compileSdkVersion
2
2
buildToolsVersion
"2
1.1.2
"
buildToolsVersion
"2
2.0.1
"
defaultConfig
{
defaultConfig
{
minSdkVersion
16
minSdkVersion
16
targetSdkVersion
2
1
targetSdkVersion
2
2
}
}
buildTypes
{
buildTypes
{
release
{
release
{
...
...
demo/src/main/AndroidManifest.xml
View file @
dd41d14a
...
@@ -25,7 +25,7 @@
...
@@ -25,7 +25,7 @@
<uses-permission
android:name=
"android.permission.ACCESS_WIFI_STATE"
/>
<uses-permission
android:name=
"android.permission.ACCESS_WIFI_STATE"
/>
<uses-permission
android:name=
"android.permission.WRITE_EXTERNAL_STORAGE"
/>
<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
<application
android:label=
"@string/application_name"
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;
...
@@ -25,7 +25,6 @@ import java.net.CookieHandler;
import
java.net.CookieManager
;
import
java.net.CookieManager
;
import
java.net.CookiePolicy
;
import
java.net.CookiePolicy
;
import
java.net.HttpURLConnection
;
import
java.net.HttpURLConnection
;
import
java.net.MalformedURLException
;
import
java.net.URL
;
import
java.net.URL
;
import
java.util.Map
;
import
java.util.Map
;
...
@@ -52,7 +51,7 @@ public class DemoUtil {
...
@@ -52,7 +51,7 @@ public class DemoUtil {
}
}
public
static
byte
[]
executePost
(
String
url
,
byte
[]
data
,
Map
<
String
,
String
>
requestProperties
)
public
static
byte
[]
executePost
(
String
url
,
byte
[]
data
,
Map
<
String
,
String
>
requestProperties
)
throws
MalformedURLException
,
IOException
{
throws
IOException
{
HttpURLConnection
urlConnection
=
null
;
HttpURLConnection
urlConnection
=
null
;
try
{
try
{
urlConnection
=
(
HttpURLConnection
)
new
URL
(
url
).
openConnection
();
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,
...
@@ -229,7 +229,8 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
return
new
DashRendererBuilder
(
this
,
userAgent
,
contentUri
.
toString
(),
return
new
DashRendererBuilder
(
this
,
userAgent
,
contentUri
.
toString
(),
new
WidevineTestMediaDrmCallback
(
contentId
),
debugTextView
,
audioCapabilities
);
new
WidevineTestMediaDrmCallback
(
contentId
),
debugTextView
,
audioCapabilities
);
case
DemoUtil
.
TYPE_HLS
:
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_M4A
:
// There are no file format differences between M4A and MP4.
case
DemoUtil
.
TYPE_MP4
:
case
DemoUtil
.
TYPE_MP4
:
return
new
ExtractorRendererBuilder
(
userAgent
,
contentUri
,
debugTextView
,
return
new
ExtractorRendererBuilder
(
userAgent
,
contentUri
,
debugTextView
,
...
@@ -239,7 +240,7 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
...
@@ -239,7 +240,7 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
new
Mp3Extractor
());
new
Mp3Extractor
());
case
DemoUtil
.
TYPE_TS
:
case
DemoUtil
.
TYPE_TS
:
return
new
ExtractorRendererBuilder
(
userAgent
,
contentUri
,
debugTextView
,
return
new
ExtractorRendererBuilder
(
userAgent
,
contentUri
,
debugTextView
,
new
TsExtractor
());
new
TsExtractor
(
0
,
audioCapabilities
));
case
DemoUtil
.
TYPE_AAC
:
case
DemoUtil
.
TYPE_AAC
:
return
new
ExtractorRendererBuilder
(
userAgent
,
contentUri
,
debugTextView
,
return
new
ExtractorRendererBuilder
(
userAgent
,
contentUri
,
debugTextView
,
new
AdtsExtractor
());
new
AdtsExtractor
());
...
...
demo/src/main/java/com/google/android/exoplayer/demo/Samples.java
View file @
dd41d14a
...
@@ -130,8 +130,6 @@ import java.util.Locale;
...
@@ -130,8 +130,6 @@ import java.util.Locale;
public
static
final
Sample
[]
MISC
=
new
Sample
[]
{
public
static
final
Sample
[]
MISC
=
new
Sample
[]
{
new
Sample
(
"Dizzy"
,
"http://html5demos.com/assets/dizzy.mp4"
,
new
Sample
(
"Dizzy"
,
"http://html5demos.com/assets/dizzy.mp4"
,
DemoUtil
.
TYPE_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/"
new
Sample
(
"Apple AAC 10s"
,
"https://devimages.apple.com.edgekey.net/"
+
"streaming/examples/bipbop_4x3/gear0/fileSequence0.aac"
,
+
"streaming/examples/bipbop_4x3/gear0/fileSequence0.aac"
,
DemoUtil
.
TYPE_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;
...
@@ -22,8 +22,6 @@ import android.media.MediaDrm.KeyRequest;
import
android.media.MediaDrm.ProvisionRequest
;
import
android.media.MediaDrm.ProvisionRequest
;
import
android.text.TextUtils
;
import
android.text.TextUtils
;
import
org.apache.http.client.ClientProtocolException
;
import
java.io.IOException
;
import
java.io.IOException
;
import
java.util.UUID
;
import
java.util.UUID
;
...
@@ -43,8 +41,7 @@ public class WidevineTestMediaDrmCallback implements MediaDrmCallback {
...
@@ -43,8 +41,7 @@ public class WidevineTestMediaDrmCallback implements MediaDrmCallback {
}
}
@Override
@Override
public
byte
[]
executeProvisionRequest
(
UUID
uuid
,
ProvisionRequest
request
)
public
byte
[]
executeProvisionRequest
(
UUID
uuid
,
ProvisionRequest
request
)
throws
IOException
{
throws
ClientProtocolException
,
IOException
{
String
url
=
request
.
getDefaultUrl
()
+
"&signedRequest="
+
new
String
(
request
.
getData
());
String
url
=
request
.
getDefaultUrl
()
+
"&signedRequest="
+
new
String
(
request
.
getData
());
return
DemoUtil
.
executePost
(
url
,
null
,
null
);
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;
...
@@ -59,7 +59,6 @@ import com.google.android.exoplayer.util.Util;
import
android.annotation.TargetApi
;
import
android.annotation.TargetApi
;
import
android.content.Context
;
import
android.content.Context
;
import
android.media.AudioFormat
;
import
android.media.MediaCodec
;
import
android.media.MediaCodec
;
import
android.media.UnsupportedSchemeException
;
import
android.media.UnsupportedSchemeException
;
import
android.os.Handler
;
import
android.os.Handler
;
...
@@ -249,7 +248,6 @@ public class DashRendererBuilder implements RendererBuilder,
...
@@ -249,7 +248,6 @@ public class DashRendererBuilder implements RendererBuilder,
// Build the audio chunk sources.
// Build the audio chunk sources.
List
<
ChunkSource
>
audioChunkSourceList
=
new
ArrayList
<
ChunkSource
>();
List
<
ChunkSource
>
audioChunkSourceList
=
new
ArrayList
<
ChunkSource
>();
List
<
String
>
audioTrackNameList
=
new
ArrayList
<
String
>();
List
<
String
>
audioTrackNameList
=
new
ArrayList
<
String
>();
int
audioEncoding
=
AudioFormat
.
ENCODING_PCM_16BIT
;
if
(
audioAdaptationSet
!=
null
)
{
if
(
audioAdaptationSet
!=
null
)
{
DataSource
audioDataSource
=
new
DefaultUriDataSource
(
userAgent
,
bandwidthMeter
);
DataSource
audioDataSource
=
new
DefaultUriDataSource
(
userAgent
,
bandwidthMeter
);
FormatEvaluator
audioEvaluator
=
new
FormatEvaluator
.
FixedEvaluator
();
FormatEvaluator
audioEvaluator
=
new
FormatEvaluator
.
FixedEvaluator
();
...
@@ -275,7 +273,6 @@ public class DashRendererBuilder implements RendererBuilder,
...
@@ -275,7 +273,6 @@ public class DashRendererBuilder implements RendererBuilder,
continue
;
continue
;
}
}
audioEncoding
=
encoding
;
for
(
int
j
=
audioRepresentations
.
size
()
-
1
;
j
>=
0
;
j
--)
{
for
(
int
j
=
audioRepresentations
.
size
()
-
1
;
j
>=
0
;
j
--)
{
if
(!
audioRepresentations
.
get
(
j
).
format
.
codecs
.
equals
(
codec
))
{
if
(!
audioRepresentations
.
get
(
j
).
format
.
codecs
.
equals
(
codec
))
{
audioTrackNameList
.
remove
(
j
);
audioTrackNameList
.
remove
(
j
);
...
@@ -303,7 +300,7 @@ public class DashRendererBuilder implements RendererBuilder,
...
@@ -303,7 +300,7 @@ public class DashRendererBuilder implements RendererBuilder,
AUDIO_BUFFER_SEGMENTS
*
BUFFER_SEGMENT_SIZE
,
true
,
mainHandler
,
player
,
AUDIO_BUFFER_SEGMENTS
*
BUFFER_SEGMENT_SIZE
,
true
,
mainHandler
,
player
,
DemoPlayer
.
TYPE_AUDIO
);
DemoPlayer
.
TYPE_AUDIO
);
audioRenderer
=
new
MediaCodecAudioTrackRenderer
(
audioSampleSource
,
drmSessionManager
,
true
,
audioRenderer
=
new
MediaCodecAudioTrackRenderer
(
audioSampleSource
,
drmSessionManager
,
true
,
mainHandler
,
player
,
audioEncoding
);
mainHandler
,
player
);
}
}
// Build the text chunk sources.
// 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;
...
@@ -57,7 +57,7 @@ import android.widget.TextView;
}
}
@Override
@Override
protected
int
doPrepare
()
throws
ExoPlaybackException
{
protected
int
doPrepare
(
long
positionUs
)
throws
ExoPlaybackException
{
maybeFail
();
maybeFail
();
return
STATE_PREPARED
;
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;
...
@@ -19,6 +19,7 @@ import com.google.android.exoplayer.MediaCodecAudioTrackRenderer;
import
com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException
;
import
com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException
;
import
com.google.android.exoplayer.MediaCodecVideoTrackRenderer
;
import
com.google.android.exoplayer.MediaCodecVideoTrackRenderer
;
import
com.google.android.exoplayer.TrackRenderer
;
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.chunk.VideoFormatSelectorUtil
;
import
com.google.android.exoplayer.demo.player.DemoPlayer.RendererBuilder
;
import
com.google.android.exoplayer.demo.player.DemoPlayer.RendererBuilder
;
import
com.google.android.exoplayer.demo.player.DemoPlayer.RendererBuilderCallback
;
import
com.google.android.exoplayer.demo.player.DemoPlayer.RendererBuilderCallback
;
...
@@ -56,15 +57,18 @@ public class HlsRendererBuilder implements RendererBuilder, ManifestCallback<Hls
...
@@ -56,15 +57,18 @@ public class HlsRendererBuilder implements RendererBuilder, ManifestCallback<Hls
private
final
String
userAgent
;
private
final
String
userAgent
;
private
final
String
url
;
private
final
String
url
;
private
final
TextView
debugTextView
;
private
final
TextView
debugTextView
;
private
final
AudioCapabilities
audioCapabilities
;
private
DemoPlayer
player
;
private
DemoPlayer
player
;
private
RendererBuilderCallback
callback
;
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
.
context
=
context
;
this
.
userAgent
=
userAgent
;
this
.
userAgent
=
userAgent
;
this
.
url
=
url
;
this
.
url
=
url
;
this
.
debugTextView
=
debugTextView
;
this
.
debugTextView
=
debugTextView
;
this
.
audioCapabilities
=
audioCapabilities
;
}
}
@Override
@Override
...
@@ -101,7 +105,7 @@ public class HlsRendererBuilder implements RendererBuilder, ManifestCallback<Hls
...
@@ -101,7 +105,7 @@ public class HlsRendererBuilder implements RendererBuilder, ManifestCallback<Hls
DataSource
dataSource
=
new
DefaultUriDataSource
(
userAgent
,
bandwidthMeter
);
DataSource
dataSource
=
new
DefaultUriDataSource
(
userAgent
,
bandwidthMeter
);
HlsChunkSource
chunkSource
=
new
HlsChunkSource
(
dataSource
,
url
,
manifest
,
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
,
HlsSampleSource
sampleSource
=
new
HlsSampleSource
(
chunkSource
,
true
,
3
,
REQUESTED_BUFFER_SIZE
,
REQUESTED_BUFFER_DURATION_MS
,
mainHandler
,
player
,
DemoPlayer
.
TYPE_VIDEO
);
REQUESTED_BUFFER_DURATION_MS
,
mainHandler
,
player
,
DemoPlayer
.
TYPE_VIDEO
);
MediaCodecVideoTrackRenderer
videoRenderer
=
new
MediaCodecVideoTrackRenderer
(
sampleSource
,
MediaCodecVideoTrackRenderer
videoRenderer
=
new
MediaCodecVideoTrackRenderer
(
sampleSource
,
...
...
demo/src/main/project.properties
View file @
dd41d14a
...
@@ -8,6 +8,6 @@
...
@@ -8,6 +8,6 @@
# project structure.
# project structure.
# Project target.
# Project target.
target
=
android-2
1
target
=
android-2
2
android.library
=
false
android.library
=
false
android.library.reference.1
=
../../../library/src/main
android.library.reference.1
=
../../../library/src/main
library/build.gradle
View file @
dd41d14a
...
@@ -15,8 +15,8 @@ apply plugin: 'com.android.library'
...
@@ -15,8 +15,8 @@ apply plugin: 'com.android.library'
apply
plugin:
'bintray-release'
apply
plugin:
'bintray-release'
android
{
android
{
compileSdkVersion
2
1
compileSdkVersion
2
2
buildToolsVersion
"2
1.1.2
"
buildToolsVersion
"2
2.0.1
"
defaultConfig
{
defaultConfig
{
// Important: ExoPlayerLib specifies a minSdkVersion of 9 because
// Important: ExoPlayerLib specifies a minSdkVersion of 9 because
...
@@ -25,7 +25,7 @@ android {
...
@@ -25,7 +25,7 @@ android {
// functionality provided by the library requires API level 16 or
// functionality provided by the library requires API level 16 or
// greater.
// greater.
minSdkVersion
9
minSdkVersion
9
targetSdkVersion
2
1
targetSdkVersion
2
2
}
}
buildTypes
{
buildTypes
{
...
...
library/src/main/AndroidManifest.xml
View file @
dd41d14a
...
@@ -27,6 +27,6 @@
...
@@ -27,6 +27,6 @@
the library may be of use on older devices. However, please note that the core video playback
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.
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>
</manifest>
library/src/main/java/com/google/android/exoplayer/DummyTrackRenderer.java
View file @
dd41d14a
...
@@ -18,14 +18,14 @@ package com.google.android.exoplayer;
...
@@ -18,14 +18,14 @@ package com.google.android.exoplayer;
/**
/**
* A {@link TrackRenderer} that does nothing.
* A {@link TrackRenderer} that does nothing.
* <p>
* <p>
* This renderer returns {@link TrackRenderer#STATE_IGNORE} from {@link #doPrepare(
)} in order to
* This renderer returns {@link TrackRenderer#STATE_IGNORE} from {@link #doPrepare(
long)} in order
*
request that it should be ignored. {@link IllegalStateException} is thrown from all methods that
*
to request that it should be ignored. {@link IllegalStateException} is thrown from all methods
* are documented to indicate that they should not be invoked unless the renderer is prepared.
*
that
are documented to indicate that they should not be invoked unless the renderer is prepared.
*/
*/
public
class
DummyTrackRenderer
extends
TrackRenderer
{
public
class
DummyTrackRenderer
extends
TrackRenderer
{
@Override
@Override
protected
int
doPrepare
()
throws
ExoPlaybackException
{
protected
int
doPrepare
(
long
positionUs
)
throws
ExoPlaybackException
{
return
STATE_IGNORE
;
return
STATE_IGNORE
;
}
}
...
...
library/src/main/java/com/google/android/exoplayer/ExoPlayer.java
View file @
dd41d14a
...
@@ -101,7 +101,7 @@ public interface ExoPlayer {
...
@@ -101,7 +101,7 @@ public interface ExoPlayer {
* The default minimum duration of data that must be buffered for playback to start or resume
* The default minimum duration of data that must be buffered for playback to start or resume
* following a user action such as a seek.
* 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
* 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;
...
@@ -264,7 +264,7 @@ import java.util.List;
boolean
prepared
=
true
;
boolean
prepared
=
true
;
for
(
int
i
=
0
;
i
<
renderers
.
length
;
i
++)
{
for
(
int
i
=
0
;
i
<
renderers
.
length
;
i
++)
{
if
(
renderers
[
i
].
getState
()
==
TrackRenderer
.
STATE_UNPREPARED
)
{
if
(
renderers
[
i
].
getState
()
==
TrackRenderer
.
STATE_UNPREPARED
)
{
int
state
=
renderers
[
i
].
prepare
();
int
state
=
renderers
[
i
].
prepare
(
positionUs
);
if
(
state
==
TrackRenderer
.
STATE_UNPREPARED
)
{
if
(
state
==
TrackRenderer
.
STATE_UNPREPARED
)
{
prepared
=
false
;
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 {
...
@@ -128,7 +128,7 @@ public final class FrameworkSampleSource implements SampleSource {
}
}
@Override
@Override
public
boolean
prepare
()
throws
IOException
{
public
boolean
prepare
(
long
positionUs
)
throws
IOException
{
if
(!
prepared
)
{
if
(!
prepared
)
{
extractor
=
new
MediaExtractor
();
extractor
=
new
MediaExtractor
();
if
(
context
!=
null
)
{
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;
...
@@ -21,9 +21,7 @@ import com.google.android.exoplayer.drm.DrmSessionManager;
import
com.google.android.exoplayer.util.MimeTypes
;
import
com.google.android.exoplayer.util.MimeTypes
;
import
android.annotation.TargetApi
;
import
android.annotation.TargetApi
;
import
android.media.AudioFormat
;
import
android.media.MediaCodec
;
import
android.media.MediaCodec
;
import
android.media.MediaFormat
;
import
android.media.audiofx.Virtualizer
;
import
android.media.audiofx.Virtualizer
;
import
android.os.Handler
;
import
android.os.Handler
;
...
@@ -71,7 +69,6 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer {
...
@@ -71,7 +69,6 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer {
private
final
EventListener
eventListener
;
private
final
EventListener
eventListener
;
private
final
AudioTrack
audioTrack
;
private
final
AudioTrack
audioTrack
;
private
final
int
encoding
;
private
int
audioSessionId
;
private
int
audioSessionId
;
private
long
currentPositionUs
;
private
long
currentPositionUs
;
...
@@ -124,50 +121,27 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer {
...
@@ -124,50 +121,27 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer {
*/
*/
public
MediaCodecAudioTrackRenderer
(
SampleSource
source
,
DrmSessionManager
drmSessionManager
,
public
MediaCodecAudioTrackRenderer
(
SampleSource
source
,
DrmSessionManager
drmSessionManager
,
boolean
playClearSamplesWithoutKeys
,
Handler
eventHandler
,
EventListener
eventListener
)
{
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
);
super
(
source
,
drmSessionManager
,
playClearSamplesWithoutKeys
,
eventHandler
,
eventListener
);
this
.
eventListener
=
eventListener
;
this
.
eventListener
=
eventListener
;
this
.
audioSessionId
=
AudioTrack
.
SESSION_ID_NOT_SET
;
this
.
audioSessionId
=
AudioTrack
.
SESSION_ID_NOT_SET
;
this
.
audioTrack
=
new
AudioTrack
();
this
.
audioTrack
=
new
AudioTrack
();
this
.
encoding
=
encoding
;
}
}
@Override
@Override
protected
DecoderInfo
getDecoderInfo
(
String
mimeType
,
boolean
requiresSecureDecoder
)
protected
DecoderInfo
getDecoderInfo
(
String
mimeType
,
boolean
requiresSecureDecoder
)
throws
DecoderQueryException
{
throws
DecoderQueryException
{
if
(
encoding
==
AudioFormat
.
ENCODING_AC3
||
encoding
==
AudioFormat
.
ENCODING_E_AC3
)
{
if
(
MimeTypes
.
isPassthroughAudio
(
mimeType
)
)
{
return
new
DecoderInfo
(
RAW_DECODER_NAME
,
true
);
return
new
DecoderInfo
(
RAW_DECODER_NAME
,
true
);
}
}
return
super
.
getDecoderInfo
(
mimeType
,
requiresSecureDecoder
);
return
super
.
getDecoderInfo
(
mimeType
,
requiresSecureDecoder
);
}
}
@Override
@Override
protected
void
configureCodec
(
MediaCodec
codec
,
String
codecName
,
MediaFormat
format
,
protected
void
configureCodec
(
MediaCodec
codec
,
String
codecName
,
android
.
media
.
MediaCrypto
crypto
)
{
android
.
media
.
Media
Format
format
,
android
.
media
.
Media
Crypto
crypto
)
{
if
(
RAW_DECODER_NAME
.
equals
(
codecName
))
{
if
(
RAW_DECODER_NAME
.
equals
(
codecName
))
{
// Override the MIME type used to configure the codec if we are using a passthrough decoder.
// 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
);
format
.
setString
(
android
.
media
.
MediaFormat
.
KEY_MIME
,
MimeTypes
.
AUDIO_RAW
);
codec
.
configure
(
format
,
null
,
crypto
,
0
);
codec
.
configure
(
format
,
null
,
crypto
,
0
);
format
.
setString
(
android
.
media
.
MediaFormat
.
KEY_MIME
,
mimeType
);
format
.
setString
(
android
.
media
.
MediaFormat
.
KEY_MIME
,
mimeType
);
...
@@ -193,8 +167,13 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer {
...
@@ -193,8 +167,13 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer {
}
}
@Override
@Override
protected
void
onOutputFormatChanged
(
MediaFormat
format
)
{
protected
void
onOutputFormatChanged
(
MediaFormat
inputFormat
,
audioTrack
.
reconfigure
(
format
,
encoding
,
0
);
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 {
...
@@ -243,9 +243,9 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
}
}
@Override
@Override
protected
int
doPrepare
()
throws
ExoPlaybackException
{
protected
int
doPrepare
(
long
positionUs
)
throws
ExoPlaybackException
{
try
{
try
{
boolean
sourcePrepared
=
source
.
prepare
();
boolean
sourcePrepared
=
source
.
prepare
(
positionUs
);
if
(!
sourcePrepared
)
{
if
(!
sourcePrepared
)
{
return
TrackRenderer
.
STATE_UNPREPARED
;
return
TrackRenderer
.
STATE_UNPREPARED
;
}
}
...
@@ -742,9 +742,11 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
...
@@ -742,9 +742,11 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
* <p>
* <p>
* The default implementation is a no-op.
* 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.
// Do nothing.
}
}
...
@@ -818,7 +820,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
...
@@ -818,7 +820,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
}
}
if
(
outputIndex
==
MediaCodec
.
INFO_OUTPUT_FORMAT_CHANGED
)
{
if
(
outputIndex
==
MediaCodec
.
INFO_OUTPUT_FORMAT_CHANGED
)
{
onOutputFormatChanged
(
codec
.
getOutputFormat
());
onOutputFormatChanged
(
format
,
codec
.
getOutputFormat
());
codecCounters
.
outputFormatChangedCount
++;
codecCounters
.
outputFormatChangedCount
++;
return
true
;
return
true
;
}
else
if
(
outputIndex
==
MediaCodec
.
INFO_OUTPUT_BUFFERS_CHANGED
)
{
}
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 {
...
@@ -381,15 +381,17 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
}
}
@Override
@Override
protected
void
onOutputFormatChanged
(
android
.
media
.
MediaFormat
format
)
{
protected
void
onOutputFormatChanged
(
MediaFormat
inputFormat
,
boolean
hasCrop
=
format
.
containsKey
(
KEY_CROP_RIGHT
)
&&
format
.
containsKey
(
KEY_CROP_LEFT
)
android
.
media
.
MediaFormat
outputFormat
)
{
&&
format
.
containsKey
(
KEY_CROP_BOTTOM
)
&&
format
.
containsKey
(
KEY_CROP_TOP
);
boolean
hasCrop
=
outputFormat
.
containsKey
(
KEY_CROP_RIGHT
)
&&
outputFormat
.
containsKey
(
KEY_CROP_LEFT
)
&&
outputFormat
.
containsKey
(
KEY_CROP_BOTTOM
)
&&
outputFormat
.
containsKey
(
KEY_CROP_TOP
);
currentWidth
=
hasCrop
currentWidth
=
hasCrop
?
format
.
getInteger
(
KEY_CROP_RIGHT
)
-
f
ormat
.
getInteger
(
KEY_CROP_LEFT
)
+
1
?
outputFormat
.
getInteger
(
KEY_CROP_RIGHT
)
-
outputF
ormat
.
getInteger
(
KEY_CROP_LEFT
)
+
1
:
f
ormat
.
getInteger
(
android
.
media
.
MediaFormat
.
KEY_WIDTH
);
:
outputF
ormat
.
getInteger
(
android
.
media
.
MediaFormat
.
KEY_WIDTH
);
currentHeight
=
hasCrop
currentHeight
=
hasCrop
?
format
.
getInteger
(
KEY_CROP_BOTTOM
)
-
f
ormat
.
getInteger
(
KEY_CROP_TOP
)
+
1
?
outputFormat
.
getInteger
(
KEY_CROP_BOTTOM
)
-
outputF
ormat
.
getInteger
(
KEY_CROP_TOP
)
+
1
:
f
ormat
.
getInteger
(
android
.
media
.
MediaFormat
.
KEY_HEIGHT
);
:
outputF
ormat
.
getInteger
(
android
.
media
.
MediaFormat
.
KEY_HEIGHT
);
}
}
@Override
@Override
...
...
library/src/main/java/com/google/android/exoplayer/SampleSource.java
View file @
dd41d14a
...
@@ -57,10 +57,11 @@ public interface SampleSource {
...
@@ -57,10 +57,11 @@ public interface SampleSource {
* and formats). If insufficient data is available then the call will return {@code false} rather
* 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.
* 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.
* @return True if the source was prepared successfully, false otherwise.
* @throws IOException If an error occurred preparing the source.
* @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.
* 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 {
...
@@ -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
* 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.
* 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.
* @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
);
Assertions
.
checkState
(
state
==
TrackRenderer
.
STATE_UNPREPARED
);
state
=
doPrepare
();
state
=
doPrepare
(
positionUs
);
Assertions
.
checkState
(
state
==
TrackRenderer
.
STATE_UNPREPARED
||
Assertions
.
checkState
(
state
==
TrackRenderer
.
STATE_UNPREPARED
||
state
==
TrackRenderer
.
STATE_PREPARED
||
state
==
TrackRenderer
.
STATE_PREPARED
||
state
==
TrackRenderer
.
STATE_IGNORE
);
state
==
TrackRenderer
.
STATE_IGNORE
);
...
@@ -127,11 +128,12 @@ public abstract class TrackRenderer implements ExoPlayerComponent {
...
@@ -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
* This method should return quickly, and should not block if the renderer is currently unable to
* make any useful progress.
* make any useful progress.
*
*
* @param positionUs The player's current playback position.
* @return The new state of the renderer. One of {@link #STATE_UNPREPARED},
* @return The new state of the renderer. One of {@link #STATE_UNPREPARED},
* {@link #STATE_PREPARED} and {@link #STATE_IGNORE}.
* {@link #STATE_PREPARED} and {@link #STATE_IGNORE}.
* @throws ExoPlaybackException If an error occurs.
* @throws ExoPlaybackException If an error occurs.
*/
*/
protected
abstract
int
doPrepare
()
throws
ExoPlaybackException
;
protected
abstract
int
doPrepare
(
long
positionUs
)
throws
ExoPlaybackException
;
/**
/**
* Enable the renderer.
* 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;
...
@@ -18,6 +18,7 @@ package com.google.android.exoplayer.audio;
import
com.google.android.exoplayer.C
;
import
com.google.android.exoplayer.C
;
import
com.google.android.exoplayer.util.Ac3Util
;
import
com.google.android.exoplayer.util.Ac3Util
;
import
com.google.android.exoplayer.util.Assertions
;
import
com.google.android.exoplayer.util.Assertions
;
import
com.google.android.exoplayer.util.MimeTypes
;
import
com.google.android.exoplayer.util.Util
;
import
com.google.android.exoplayer.util.Util
;
import
android.annotation.TargetApi
;
import
android.annotation.TargetApi
;
...
@@ -315,24 +316,21 @@ public final class AudioTrack {
...
@@ -315,24 +316,21 @@ public final class AudioTrack {
}
}
/**
/**
* Reconfigures the audio track to play back media in {@code format}
. The encoding is assumed to
* Reconfigures the audio track to play back media in {@code format}
, inferring a buffer size from
*
be {@link AudioFormat#ENCODING_PCM_16BIT}
.
*
the format
.
*/
*/
public
void
reconfigure
(
MediaFormat
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
* Reconfigures the audio track to play back media in {@code format}.
* {@link #handleBuffer} must use the specified {@code encoding}, which should be a constant from
* {@link AudioFormat}.
*
*
* @param format Specifies the channel count and sample rate to play back.
* @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
* @param specifiedBufferSize A specific size for the playback buffer in bytes, or 0 to use a
* size inferred from the format.
* 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
channelCount
=
format
.
getInteger
(
MediaFormat
.
KEY_CHANNEL_COUNT
);
int
channelConfig
;
int
channelConfig
;
switch
(
channelCount
)
{
switch
(
channelCount
)
{
...
@@ -353,8 +351,10 @@ public final class AudioTrack {
...
@@ -353,8 +351,10 @@ public final class AudioTrack {
}
}
int
sampleRate
=
format
.
getInteger
(
MediaFormat
.
KEY_SAMPLE_RATE
);
int
sampleRate
=
format
.
getInteger
(
MediaFormat
.
KEY_SAMPLE_RATE
);
String
mimeType
=
format
.
getString
(
MediaFormat
.
KEY_MIME
);
// TODO: Does channelConfig determine channelCount?
// TODO: Does channelConfig determine channelCount?
int
encoding
=
MimeTypes
.
getEncodingForMimeType
(
mimeType
);
boolean
isAc3
=
encoding
==
C
.
ENCODING_AC3
||
encoding
==
C
.
ENCODING_E_AC3
;
boolean
isAc3
=
encoding
==
C
.
ENCODING_AC3
||
encoding
==
C
.
ENCODING_E_AC3
;
if
(
isInitialized
()
&&
this
.
sampleRate
==
sampleRate
&&
this
.
channelConfig
==
channelConfig
if
(
isInitialized
()
&&
this
.
sampleRate
==
sampleRate
&&
this
.
channelConfig
==
channelConfig
&&
!
this
.
isAc3
&&
!
isAc3
)
{
&&
!
this
.
isAc3
&&
!
isAc3
)
{
...
@@ -423,10 +423,21 @@ public final class AudioTrack {
...
@@ -423,10 +423,21 @@ public final class AudioTrack {
return
RESULT_BUFFER_CONSUMED
;
return
RESULT_BUFFER_CONSUMED
;
}
}
// As a workaround for an issue where an an AC-3 audio track continues to play data written
// Workarounds for issues with AC-3 passthrough AudioTracks on API versions 21/22:
// while it is paused, stop writing so its buffer empties. See [Internal: b/18899620].
if
(
Util
.
SDK_INT
<=
22
&&
isAc3
)
{
if
(
isAc3
&&
audioTrack
.
getPlayState
()
==
android
.
media
.
AudioTrack
.
PLAYSTATE_PAUSED
)
{
// An AC-3 audio track continues to play data written while it is paused. Stop writing so its
return
0
;
// 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
;
int
result
=
0
;
...
@@ -637,7 +648,8 @@ public final class AudioTrack {
...
@@ -637,7 +648,8 @@ public final class AudioTrack {
}
}
if
(
systemClockUs
-
lastTimestampSampleTimeUs
>=
MIN_TIMESTAMP_SAMPLE_INTERVAL_US
)
{
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
)
{
if
(
audioTimestampSet
)
{
// Perform sanity checks on the timestamp.
// Perform sanity checks on the timestamp.
long
audioTimestampUs
=
audioTrackUtil
.
getTimestampNanoTime
()
/
1000
;
long
audioTimestampUs
=
audioTrackUtil
.
getTimestampNanoTime
()
/
1000
;
...
@@ -739,7 +751,7 @@ public final class AudioTrack {
...
@@ -739,7 +751,7 @@ public final class AudioTrack {
private
static
class
AudioTrackUtil
{
private
static
class
AudioTrackUtil
{
protected
android
.
media
.
AudioTrack
audioTrack
;
protected
android
.
media
.
AudioTrack
audioTrack
;
private
boolean
enablePassthroughWorkaround
;
private
boolean
isPassthrough
;
private
int
sampleRate
;
private
int
sampleRate
;
private
long
lastRawPlaybackHeadPosition
;
private
long
lastRawPlaybackHeadPosition
;
private
long
rawPlaybackHeadWrapCount
;
private
long
rawPlaybackHeadWrapCount
;
...
@@ -749,14 +761,11 @@ public final class AudioTrack {
...
@@ -749,14 +761,11 @@ public final class AudioTrack {
* Reconfigures the audio track utility helper to use the specified {@code audioTrack}.
* Reconfigures the audio track utility helper to use the specified {@code audioTrack}.
*
*
* @param audioTrack The audio track to wrap.
* @param audioTrack The audio track to wrap.
* @param enablePassthroughWorkaround Whether to work around an issue where the playback head
* @param isPassthrough Whether the audio track is used for passthrough (e.g. AC-3) playback.
* position jumps back to zero on a paused passthrough/direct audio track. See
* [Internal: b/19187573].
*/
*/
public
void
reconfigure
(
android
.
media
.
AudioTrack
audioTrack
,
public
void
reconfigure
(
android
.
media
.
AudioTrack
audioTrack
,
boolean
isPassthrough
)
{
boolean
enablePassthroughWorkaround
)
{
this
.
audioTrack
=
audioTrack
;
this
.
audioTrack
=
audioTrack
;
this
.
enablePassthroughWorkaround
=
enablePassthroughWorkaround
;
this
.
isPassthrough
=
isPassthrough
;
lastRawPlaybackHeadPosition
=
0
;
lastRawPlaybackHeadPosition
=
0
;
rawPlaybackHeadWrapCount
=
0
;
rawPlaybackHeadWrapCount
=
0
;
passthroughWorkaroundPauseOffset
=
0
;
passthroughWorkaroundPauseOffset
=
0
;
...
@@ -767,14 +776,14 @@ public final class AudioTrack {
...
@@ -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
* 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
* around an issue
on platform API versions 21/22 where AC-3 audio tracks can't be paused, so we
*
paused. In this case, they should still behave as if they have pending data, otherwis
e
*
empty their buffers when paused. In this case, they should still behave as if they hav
e
* writing will never resume.
*
pending data, otherwise
writing will never resume.
*
*
* @see #handleBuffer
* @see #handleBuffer
*/
*/
public
boolean
overrideHasPendingData
()
{
public
boolean
overrideHasPendingData
()
{
return
enablePassthroughWorkaround
return
Util
.
SDK_INT
<=
22
&&
isPassthrough
&&
audioTrack
.
getPlayState
()
==
android
.
media
.
AudioTrack
.
PLAYSTATE_PAUSED
&&
audioTrack
.
getPlayState
()
==
android
.
media
.
AudioTrack
.
PLAYSTATE_PAUSED
&&
audioTrack
.
getPlaybackHeadPosition
()
==
0
;
&&
audioTrack
.
getPlaybackHeadPosition
()
==
0
;
}
}
...
@@ -790,8 +799,16 @@ public final class AudioTrack {
...
@@ -790,8 +799,16 @@ public final class AudioTrack {
*/
*/
public
long
getPlaybackHeadPosition
()
{
public
long
getPlaybackHeadPosition
()
{
long
rawPlaybackHeadPosition
=
0xFFFFFFFF
L
&
audioTrack
.
getPlaybackHeadPosition
();
long
rawPlaybackHeadPosition
=
0xFFFFFFFF
L
&
audioTrack
.
getPlaybackHeadPosition
();
if
(
enablePassthroughWorkaround
)
{
if
(
Util
.
SDK_INT
<=
22
&&
isPassthrough
)
{
if
(
audioTrack
.
getPlayState
()
==
android
.
media
.
AudioTrack
.
PLAYSTATE_PAUSED
// 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
)
{
&&
rawPlaybackHeadPosition
==
0
)
{
passthroughWorkaroundPauseOffset
=
lastRawPlaybackHeadPosition
;
passthroughWorkaroundPauseOffset
=
lastRawPlaybackHeadPosition
;
}
}
...
@@ -868,9 +885,8 @@ public final class AudioTrack {
...
@@ -868,9 +885,8 @@ public final class AudioTrack {
}
}
@Override
@Override
public
void
reconfigure
(
android
.
media
.
AudioTrack
audioTrack
,
public
void
reconfigure
(
android
.
media
.
AudioTrack
audioTrack
,
boolean
isPassthrough
)
{
boolean
enablePassthroughWorkaround
)
{
super
.
reconfigure
(
audioTrack
,
isPassthrough
);
super
.
reconfigure
(
audioTrack
,
enablePassthroughWorkaround
);
rawTimestampFramePositionWrapCount
=
0
;
rawTimestampFramePositionWrapCount
=
0
;
lastRawTimestampFramePosition
=
0
;
lastRawTimestampFramePosition
=
0
;
lastTimestampFramePosition
=
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 {
...
@@ -87,7 +87,7 @@ public class ChunkSampleSource implements SampleSource, Loader.Callback {
private
long
currentLoadStartTimeMs
;
private
long
currentLoadStartTimeMs
;
private
MediaFormat
downstreamMediaFormat
;
private
MediaFormat
downstreamMediaFormat
;
private
volatile
Format
downstreamFormat
;
private
Format
downstreamFormat
;
public
ChunkSampleSource
(
ChunkSource
chunkSource
,
LoadControl
loadControl
,
public
ChunkSampleSource
(
ChunkSource
chunkSource
,
LoadControl
loadControl
,
int
bufferSizeContribution
,
boolean
frameAccurateSeeking
)
{
int
bufferSizeContribution
,
boolean
frameAccurateSeeking
)
{
...
@@ -120,18 +120,8 @@ public class ChunkSampleSource implements SampleSource, Loader.Callback {
...
@@ -120,18 +120,8 @@ public class ChunkSampleSource implements SampleSource, Loader.Callback {
pendingResetPositionUs
=
NO_RESET_PENDING
;
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
@Override
public
boolean
prepare
()
{
public
boolean
prepare
(
long
positionUs
)
{
Assertions
.
checkState
(
state
==
STATE_UNPREPARED
);
Assertions
.
checkState
(
state
==
STATE_UNPREPARED
);
loader
=
new
Loader
(
"Loader:"
+
chunkSource
.
getTrackInfo
().
mimeType
);
loader
=
new
Loader
(
"Loader:"
+
chunkSource
.
getTrackInfo
().
mimeType
);
state
=
STATE_PREPARED
;
state
=
STATE_PREPARED
;
...
...
library/src/main/java/com/google/android/exoplayer/chunk/FormatEvaluator.java
View file @
dd41d14a
...
@@ -84,7 +84,7 @@ public interface FormatEvaluator {
...
@@ -84,7 +84,7 @@ public interface FormatEvaluator {
/**
/**
* Always selects the first format.
* Always selects the first format.
*/
*/
public
static
class
FixedEvaluator
implements
FormatEvaluator
{
public
static
final
class
FixedEvaluator
implements
FormatEvaluator
{
@Override
@Override
public
void
enable
()
{
public
void
enable
()
{
...
@@ -107,7 +107,7 @@ public interface FormatEvaluator {
...
@@ -107,7 +107,7 @@ public interface FormatEvaluator {
/**
/**
* Selects randomly between the available formats.
* Selects randomly between the available formats.
*/
*/
public
static
class
RandomEvaluator
implements
FormatEvaluator
{
public
static
final
class
RandomEvaluator
implements
FormatEvaluator
{
private
final
Random
random
;
private
final
Random
random
;
...
@@ -145,7 +145,7 @@ public interface FormatEvaluator {
...
@@ -145,7 +145,7 @@ public interface FormatEvaluator {
* reference implementation only. It is recommended that application developers implement their
* reference implementation only. It is recommended that application developers implement their
* own adaptive evaluator to more precisely suit their use case.
* 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
;
public
static
final
int
DEFAULT_MAX_INITIAL_BITRATE
=
800000
;
...
@@ -259,8 +259,9 @@ public interface FormatEvaluator {
...
@@ -259,8 +259,9 @@ public interface FormatEvaluator {
/**
/**
* Compute the ideal format ignoring buffer health.
* Compute the ideal format ignoring buffer health.
*/
*/
protected
Format
determineIdealFormat
(
Format
[]
formats
,
long
bitrateEstimate
)
{
private
Format
determineIdealFormat
(
Format
[]
formats
,
long
bitrateEstimate
)
{
long
effectiveBitrate
=
computeEffectiveBitrateEstimate
(
bitrateEstimate
);
long
effectiveBitrate
=
bitrateEstimate
==
BandwidthMeter
.
NO_ESTIMATE
?
maxInitialBitrate
:
(
long
)
(
bitrateEstimate
*
bandwidthFraction
);
for
(
int
i
=
0
;
i
<
formats
.
length
;
i
++)
{
for
(
int
i
=
0
;
i
<
formats
.
length
;
i
++)
{
Format
format
=
formats
[
i
];
Format
format
=
formats
[
i
];
if
(
format
.
bitrate
<=
effectiveBitrate
)
{
if
(
format
.
bitrate
<=
effectiveBitrate
)
{
...
@@ -271,14 +272,6 @@ public interface FormatEvaluator {
...
@@ -271,14 +272,6 @@ public interface FormatEvaluator {
return
formats
[
formats
.
length
-
1
];
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 {
...
@@ -419,7 +419,9 @@ public class DashChunkSource implements ChunkSource {
(
ChunkIndex
)
initializationChunk
.
getSeekMap
(),
(
ChunkIndex
)
initializationChunk
.
getSeekMap
(),
initializationChunk
.
dataSpec
.
uri
.
toString
());
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
();
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;
...
@@ -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.upstream.UriLoadable
;
import
com.google.android.exoplayer.util.Assertions
;
import
com.google.android.exoplayer.util.Assertions
;
import
com.google.android.exoplayer.util.MimeTypes
;
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.UriUtil
;
import
com.google.android.exoplayer.util.Util
;
import
com.google.android.exoplayer.util.Util
;
import
android.text.TextUtils
;
import
android.text.TextUtils
;
import
android.util.Base64
;
import
org.xml.sax.helpers.DefaultHandler
;
import
org.xml.sax.helpers.DefaultHandler
;
import
org.xmlpull.v1.XmlPullParser
;
import
org.xmlpull.v1.XmlPullParser
;
...
@@ -41,6 +43,7 @@ import java.util.ArrayList;
...
@@ -41,6 +43,7 @@ import java.util.ArrayList;
import
java.util.Collections
;
import
java.util.Collections
;
import
java.util.Comparator
;
import
java.util.Comparator
;
import
java.util.List
;
import
java.util.List
;
import
java.util.UUID
;
import
java.util.regex.Matcher
;
import
java.util.regex.Matcher
;
import
java.util.regex.Pattern
;
import
java.util.regex.Pattern
;
...
@@ -270,11 +273,27 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
...
@@ -270,11 +273,27 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
protected
ContentProtection
parseContentProtection
(
XmlPullParser
xpp
)
protected
ContentProtection
parseContentProtection
(
XmlPullParser
xpp
)
throws
XmlPullParserException
,
IOException
{
throws
XmlPullParserException
,
IOException
{
String
schemeIdUri
=
xpp
.
getAttributeValue
(
null
,
"schemeIdUri"
);
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
)
{
protected
ContentProtection
buildContentProtection
(
String
schemeIdUri
,
UUID
uuid
,
byte
[]
data
)
{
return
new
ContentProtection
(
schemeIdUri
,
null
,
null
);
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
...
@@ -72,6 +72,7 @@ public class ExtractorSampleSource implements SampleSource, ExtractorOutput, Loa
private
boolean
prepared
;
private
boolean
prepared
;
private
int
enabledTrackCount
;
private
int
enabledTrackCount
;
private
TrackInfo
[]
trackInfos
;
private
TrackInfo
[]
trackInfos
;
private
long
maxTrackDurationUs
;
private
boolean
[]
pendingMediaFormat
;
private
boolean
[]
pendingMediaFormat
;
private
boolean
[]
pendingDiscontinuities
;
private
boolean
[]
pendingDiscontinuities
;
private
boolean
[]
trackEnabledStates
;
private
boolean
[]
trackEnabledStates
;
...
@@ -137,7 +138,7 @@ public class ExtractorSampleSource implements SampleSource, ExtractorOutput, Loa
...
@@ -137,7 +138,7 @@ public class ExtractorSampleSource implements SampleSource, ExtractorOutput, Loa
}
}
@Override
@Override
public
boolean
prepare
()
throws
IOException
{
public
boolean
prepare
(
long
positionUs
)
throws
IOException
{
if
(
prepared
)
{
if
(
prepared
)
{
return
true
;
return
true
;
}
}
...
@@ -156,9 +157,13 @@ public class ExtractorSampleSource implements SampleSource, ExtractorOutput, Loa
...
@@ -156,9 +157,13 @@ public class ExtractorSampleSource implements SampleSource, ExtractorOutput, Loa
pendingDiscontinuities
=
new
boolean
[
trackCount
];
pendingDiscontinuities
=
new
boolean
[
trackCount
];
pendingMediaFormat
=
new
boolean
[
trackCount
];
pendingMediaFormat
=
new
boolean
[
trackCount
];
trackInfos
=
new
TrackInfo
[
trackCount
];
trackInfos
=
new
TrackInfo
[
trackCount
];
maxTrackDurationUs
=
C
.
UNKNOWN_TIME_US
;
for
(
int
i
=
0
;
i
<
trackCount
;
i
++)
{
for
(
int
i
=
0
;
i
<
trackCount
;
i
++)
{
MediaFormat
format
=
sampleQueues
.
valueAt
(
i
).
getFormat
();
MediaFormat
format
=
sampleQueues
.
valueAt
(
i
).
getFormat
();
trackInfos
[
i
]
=
new
TrackInfo
(
format
.
mimeType
,
format
.
durationUs
);
trackInfos
[
i
]
=
new
TrackInfo
(
format
.
mimeType
,
format
.
durationUs
);
if
(
format
.
durationUs
!=
C
.
UNKNOWN_TIME_US
&&
format
.
durationUs
>
maxTrackDurationUs
)
{
maxTrackDurationUs
=
format
.
durationUs
;
}
}
}
prepared
=
true
;
prepared
=
true
;
return
true
;
return
true
;
...
@@ -448,6 +453,11 @@ public class ExtractorSampleSource implements SampleSource, ExtractorOutput, Loa
...
@@ -448,6 +453,11 @@ public class ExtractorSampleSource implements SampleSource, ExtractorOutput, Loa
loadable
=
createLoadableFromStart
();
loadable
=
createLoadableFromStart
();
}
else
{
}
else
{
Assertions
.
checkState
(
isPendingReset
());
Assertions
.
checkState
(
isPendingReset
());
if
(
maxTrackDurationUs
!=
C
.
UNKNOWN_TIME_US
&&
pendingResetPositionUs
>=
maxTrackDurationUs
)
{
loadingFinished
=
true
;
pendingResetPositionUs
=
NO_RESET_PENDING
;
return
;
}
loadable
=
createLoadableFromPositionUs
(
pendingResetPositionUs
);
loadable
=
createLoadableFromPositionUs
(
pendingResetPositionUs
);
pendingResetPositionUs
=
NO_RESET_PENDING
;
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;
...
@@ -137,7 +137,7 @@ import com.google.android.exoplayer.util.Util;
}
}
long
position
=
(
long
)
((
1
f
/
256
)
*
fx
*
sizeBytes
)
+
firstFramePosition
;
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
@Override
...
...
library/src/main/java/com/google/android/exoplayer/extractor/mp4/AtomParsers.java
View file @
dd41d14a
...
@@ -534,8 +534,18 @@ import java.util.List;
...
@@ -534,8 +534,18 @@ import java.util.List;
childPosition
+=
childAtomSize
;
childPosition
+=
childAtomSize
;
}
}
out
.
mediaFormat
=
MediaFormat
.
createAudioFormat
(
// Set the MIME type for ac-3/ec-3 atoms even if the dac3/dec3 child atom is missing.
MimeTypes
.
AUDIO_AAC
,
sampleSize
,
durationUs
,
channelCount
,
sampleRate
,
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
));
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 @@
...
@@ -16,6 +16,7 @@
package
com
.
google
.
android
.
exoplayer
.
extractor
.
ts
;
package
com
.
google
.
android
.
exoplayer
.
extractor
.
ts
;
import
com.google.android.exoplayer.C
;
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.Extractor
;
import
com.google.android.exoplayer.extractor.ExtractorInput
;
import
com.google.android.exoplayer.extractor.ExtractorInput
;
import
com.google.android.exoplayer.extractor.ExtractorOutput
;
import
com.google.android.exoplayer.extractor.ExtractorOutput
;
...
@@ -43,7 +44,7 @@ public final class TsExtractor implements Extractor, SeekMap {
...
@@ -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_AAC
=
0x0F
;
private
static
final
int
TS_STREAM_TYPE_ATSC_AC3
=
0x81
;
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_H264
=
0x1B
;
private
static
final
int
TS_STREAM_TYPE_ID3
=
0x15
;
private
static
final
int
TS_STREAM_TYPE_ID3
=
0x15
;
private
static
final
int
TS_STREAM_TYPE_EIA608
=
0x100
;
// 0xFF + 1
private
static
final
int
TS_STREAM_TYPE_EIA608
=
0x100
;
// 0xFF + 1
...
@@ -52,6 +53,7 @@ public final class TsExtractor implements Extractor, SeekMap {
...
@@ -52,6 +53,7 @@ public final class TsExtractor implements Extractor, SeekMap {
private
final
ParsableByteArray
tsPacketBuffer
;
private
final
ParsableByteArray
tsPacketBuffer
;
private
final
SparseBooleanArray
streamTypes
;
private
final
SparseBooleanArray
streamTypes
;
private
final
SparseBooleanArray
allowedPassthroughStreamTypes
;
private
final
SparseArray
<
TsPayloadReader
>
tsPayloadReaders
;
// Indexed by pid
private
final
SparseArray
<
TsPayloadReader
>
tsPayloadReaders
;
// Indexed by pid
private
final
long
firstSampleTimestampUs
;
private
final
long
firstSampleTimestampUs
;
private
final
ParsableBitArray
tsScratch
;
private
final
ParsableBitArray
tsScratch
;
...
@@ -62,14 +64,15 @@ public final class TsExtractor implements Extractor, SeekMap {
...
@@ -62,14 +64,15 @@ public final class TsExtractor implements Extractor, SeekMap {
private
long
lastPts
;
private
long
lastPts
;
public
TsExtractor
()
{
public
TsExtractor
()
{
this
(
0
);
this
(
0
,
null
);
}
}
public
TsExtractor
(
long
firstSampleTimestampUs
)
{
public
TsExtractor
(
long
firstSampleTimestampUs
,
AudioCapabilities
audioCapabilities
)
{
this
.
firstSampleTimestampUs
=
firstSampleTimestampUs
;
this
.
firstSampleTimestampUs
=
firstSampleTimestampUs
;
tsScratch
=
new
ParsableBitArray
(
new
byte
[
3
]);
tsScratch
=
new
ParsableBitArray
(
new
byte
[
3
]);
tsPacketBuffer
=
new
ParsableByteArray
(
TS_PACKET_SIZE
);
tsPacketBuffer
=
new
ParsableByteArray
(
TS_PACKET_SIZE
);
streamTypes
=
new
SparseBooleanArray
();
streamTypes
=
new
SparseBooleanArray
();
allowedPassthroughStreamTypes
=
getPassthroughStreamTypes
(
audioCapabilities
);
tsPayloadReaders
=
new
SparseArray
<
TsPayloadReader
>();
tsPayloadReaders
=
new
SparseArray
<
TsPayloadReader
>();
tsPayloadReaders
.
put
(
TS_PAT_PID
,
new
PatReader
());
tsPayloadReaders
.
put
(
TS_PAT_PID
,
new
PatReader
());
lastPts
=
Long
.
MIN_VALUE
;
lastPts
=
Long
.
MIN_VALUE
;
...
@@ -175,6 +178,24 @@ public final class TsExtractor implements Extractor, SeekMap {
...
@@ -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.
* Parses TS packet payload data.
*/
*/
private
abstract
static
class
TsPayloadReader
{
private
abstract
static
class
TsPayloadReader
{
...
@@ -308,13 +329,17 @@ public final class TsExtractor implements Extractor, SeekMap {
...
@@ -308,13 +329,17 @@ public final class TsExtractor implements Extractor, SeekMap {
continue
;
continue
;
}
}
// TODO: Detect and read DVB AC-3 streams with Ac3Reader.
ElementaryStreamReader
pesPayloadReader
=
null
;
ElementaryStreamReader
pesPayloadReader
=
null
;
switch
(
streamType
)
{
switch
(
streamType
)
{
case
TS_STREAM_TYPE_AAC:
case
TS_STREAM_TYPE_AAC:
pesPayloadReader
=
new
AdtsReader
(
output
.
track
(
TS_STREAM_TYPE_AAC
));
pesPayloadReader
=
new
AdtsReader
(
output
.
track
(
TS_STREAM_TYPE_AAC
));
break
;
break
;
case
TS_STREAM_TYPE_ATSC_E_AC3:
case
TS_STREAM_TYPE_ATSC_AC3:
case
TS_STREAM_TYPE_ATSC_AC3:
case
TS_STREAM_TYPE_DVB_AC3:
if
(!
allowedPassthroughStreamTypes
.
get
(
streamType
))
{
continue
;
}
pesPayloadReader
=
new
Ac3Reader
(
output
.
track
(
streamType
));
pesPayloadReader
=
new
Ac3Reader
(
output
.
track
(
streamType
));
break
;
break
;
case
TS_STREAM_TYPE_H264:
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;
...
@@ -17,6 +17,7 @@ package com.google.android.exoplayer.hls;
import
com.google.android.exoplayer.C
;
import
com.google.android.exoplayer.C
;
import
com.google.android.exoplayer.MediaFormat
;
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.BaseChunkSampleSourceEventListener
;
import
com.google.android.exoplayer.chunk.Chunk
;
import
com.google.android.exoplayer.chunk.Chunk
;
import
com.google.android.exoplayer.chunk.DataChunk
;
import
com.google.android.exoplayer.chunk.DataChunk
;
...
@@ -125,6 +126,7 @@ public class HlsChunkSource {
...
@@ -125,6 +126,7 @@ public class HlsChunkSource {
private
final
int
maxHeight
;
private
final
int
maxHeight
;
private
final
long
minBufferDurationToSwitchUpUs
;
private
final
long
minBufferDurationToSwitchUpUs
;
private
final
long
maxBufferDurationToSwitchDownUs
;
private
final
long
maxBufferDurationToSwitchDownUs
;
private
final
AudioCapabilities
audioCapabilities
;
/* package */
byte
[]
scratchSpace
;
/* package */
byte
[]
scratchSpace
;
/* package */
final
HlsMediaPlaylist
[]
mediaPlaylists
;
/* package */
final
HlsMediaPlaylist
[]
mediaPlaylists
;
...
@@ -140,9 +142,11 @@ public class HlsChunkSource {
...
@@ -140,9 +142,11 @@ public class HlsChunkSource {
private
byte
[]
encryptionIv
;
private
byte
[]
encryptionIv
;
public
HlsChunkSource
(
DataSource
dataSource
,
String
playlistUrl
,
HlsPlaylist
playlist
,
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
,
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 {
...
@@ -160,13 +164,17 @@ public class HlsChunkSource {
* for a switch to a higher quality variant to be considered.
* for a switch to a higher quality variant to be considered.
* @param maxBufferDurationToSwitchDownMs The maximum duration of media that needs to be buffered
* @param maxBufferDurationToSwitchDownMs The maximum duration of media that needs to be buffered
* for a switch to a lower quality variant to be considered.
* 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
,
public
HlsChunkSource
(
DataSource
dataSource
,
String
playlistUrl
,
HlsPlaylist
playlist
,
BandwidthMeter
bandwidthMeter
,
int
[]
variantIndices
,
int
adaptiveMode
,
BandwidthMeter
bandwidthMeter
,
int
[]
variantIndices
,
int
adaptiveMode
,
long
minBufferDurationToSwitchUpMs
,
long
maxBufferDurationToSwitchDownMs
)
{
long
minBufferDurationToSwitchUpMs
,
long
maxBufferDurationToSwitchDownMs
,
AudioCapabilities
audioCapabilities
)
{
this
.
dataSource
=
dataSource
;
this
.
dataSource
=
dataSource
;
this
.
bandwidthMeter
=
bandwidthMeter
;
this
.
bandwidthMeter
=
bandwidthMeter
;
this
.
adaptiveMode
=
adaptiveMode
;
this
.
adaptiveMode
=
adaptiveMode
;
this
.
audioCapabilities
=
audioCapabilities
;
minBufferDurationToSwitchUpUs
=
minBufferDurationToSwitchUpMs
*
1000
;
minBufferDurationToSwitchUpUs
=
minBufferDurationToSwitchUpMs
*
1000
;
maxBufferDurationToSwitchDownUs
=
maxBufferDurationToSwitchDownMs
*
1000
;
maxBufferDurationToSwitchDownUs
=
maxBufferDurationToSwitchDownMs
*
1000
;
baseUri
=
playlist
.
baseUri
;
baseUri
=
playlist
.
baseUri
;
...
@@ -235,16 +243,14 @@ public class HlsChunkSource {
...
@@ -235,16 +243,14 @@ public class HlsChunkSource {
public
Chunk
getChunkOperation
(
TsChunk
previousTsChunk
,
long
seekPositionUs
,
public
Chunk
getChunkOperation
(
TsChunk
previousTsChunk
,
long
seekPositionUs
,
long
playbackPositionUs
)
{
long
playbackPositionUs
)
{
int
nextFormatIndex
;
int
nextFormatIndex
;
boolean
switchingVariant
;
boolean
switchingVariantSpliced
;
boolean
switchingVariantSpliced
;
if
(
adaptiveMode
==
ADAPTIVE_MODE_NONE
)
{
if
(
adaptiveMode
==
ADAPTIVE_MODE_NONE
)
{
nextFormatIndex
=
formatIndex
;
nextFormatIndex
=
formatIndex
;
switchingVariant
=
false
;
switchingVariantSpliced
=
false
;
switchingVariantSpliced
=
false
;
}
else
{
}
else
{
nextFormatIndex
=
getNextFormatIndex
(
previousTsChunk
,
playbackPositionUs
);
nextFormatIndex
=
getNextFormatIndex
(
previousTsChunk
,
playbackPositionUs
);
switchingVariant
=
nextFormatIndex
!=
formatIndex
;
switchingVariant
Spliced
=
nextFormatIndex
!=
formatIndex
switchingVariantSpliced
=
switchingVariant
&&
adaptiveMode
==
ADAPTIVE_MODE_SPLICE
;
&&
adaptiveMode
==
ADAPTIVE_MODE_SPLICE
;
}
}
int
variantIndex
=
getVariantIndex
(
enabledFormats
[
nextFormatIndex
]);
int
variantIndex
=
getVariantIndex
(
enabledFormats
[
nextFormatIndex
]);
...
@@ -331,10 +337,11 @@ public class HlsChunkSource {
...
@@ -331,10 +337,11 @@ public class HlsChunkSource {
// Configure the extractor that will read the chunk.
// Configure the extractor that will read the chunk.
HlsExtractorWrapper
extractorWrapper
;
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
)
Extractor
extractor
=
chunkUri
.
getLastPathSegment
().
endsWith
(
AAC_FILE_EXTENSION
)
?
new
AdtsExtractor
(
startTimeUs
)
?
new
AdtsExtractor
(
startTimeUs
)
:
new
TsExtractor
(
startTimeUs
);
:
new
TsExtractor
(
startTimeUs
,
audioCapabilities
);
extractorWrapper
=
new
HlsExtractorWrapper
(
trigger
,
format
,
startTimeUs
,
extractor
,
extractorWrapper
=
new
HlsExtractorWrapper
(
trigger
,
format
,
startTimeUs
,
extractor
,
switchingVariantSpliced
);
switchingVariantSpliced
);
}
else
{
}
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 {
...
@@ -125,15 +125,12 @@ public class HlsSampleSource implements SampleSource, Loader.Callback {
}
}
@Override
@Override
public
boolean
prepare
()
throws
IOException
{
public
boolean
prepare
(
long
positionUs
)
throws
IOException
{
if
(
prepared
)
{
if
(
prepared
)
{
return
true
;
return
true
;
}
}
if
(
loader
==
null
)
{
loader
=
new
Loader
(
"Loader:HLS"
);
}
continueBufferingInternal
();
if
(!
extractors
.
isEmpty
())
{
if
(!
extractors
.
isEmpty
())
{
// We're not prepared, but we might have loaded what we need.
HlsExtractorWrapper
extractor
=
extractors
.
getFirst
();
HlsExtractorWrapper
extractor
=
extractors
.
getFirst
();
if
(
extractor
.
isPrepared
())
{
if
(
extractor
.
isPrepared
())
{
trackCount
=
extractor
.
getTrackCount
();
trackCount
=
extractor
.
getTrackCount
();
...
@@ -146,12 +143,23 @@ public class HlsSampleSource implements SampleSource, Loader.Callback {
...
@@ -146,12 +143,23 @@ public class HlsSampleSource implements SampleSource, Loader.Callback {
trackInfos
[
i
]
=
new
TrackInfo
(
format
.
mimeType
,
chunkSource
.
getDurationUs
());
trackInfos
[
i
]
=
new
TrackInfo
(
format
.
mimeType
,
chunkSource
.
getDurationUs
());
}
}
prepared
=
true
;
prepared
=
true
;
return
true
;
}
}
}
}
if
(!
prepared
)
{
// We're not prepared and we haven't loaded what we need.
maybeThrowLoadableException
();
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
@Override
...
@@ -345,7 +353,12 @@ public class HlsSampleSource implements SampleSource, Loader.Callback {
...
@@ -345,7 +353,12 @@ public class HlsSampleSource implements SampleSource, Loader.Callback {
if
(!
currentLoadableExceptionFatal
)
{
if
(!
currentLoadableExceptionFatal
)
{
clearCurrentLoadable
();
clearCurrentLoadable
();
}
}
maybeStartLoading
();
if
(
enabledTrackCount
>
0
)
{
maybeStartLoading
();
}
else
{
clearState
();
allocator
.
trim
(
0
);
}
}
}
@Override
@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
...
@@ -90,9 +90,9 @@ public class MetadataTrackRenderer<T> extends TrackRenderer implements Callback
}
}
@Override
@Override
protected
int
doPrepare
()
throws
ExoPlaybackException
{
protected
int
doPrepare
(
long
positionUs
)
throws
ExoPlaybackException
{
try
{
try
{
boolean
sourcePrepared
=
source
.
prepare
();
boolean
sourcePrepared
=
source
.
prepare
(
positionUs
);
if
(!
sourcePrepared
)
{
if
(!
sourcePrepared
)
{
return
TrackRenderer
.
STATE_UNPREPARED
;
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 {
...
@@ -80,9 +80,9 @@ public class TextTrackRenderer extends TrackRenderer implements Callback {
}
}
@Override
@Override
protected
int
doPrepare
()
throws
ExoPlaybackException
{
protected
int
doPrepare
(
long
positionUs
)
throws
ExoPlaybackException
{
try
{
try
{
boolean
sourcePrepared
=
source
.
prepare
();
boolean
sourcePrepared
=
source
.
prepare
(
positionUs
);
if
(!
sourcePrepared
)
{
if
(!
sourcePrepared
)
{
return
TrackRenderer
.
STATE_UNPREPARED
;
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 {
...
@@ -90,9 +90,9 @@ public class Eia608TrackRenderer extends TrackRenderer implements Callback {
}
}
@Override
@Override
protected
int
doPrepare
()
throws
ExoPlaybackException
{
protected
int
doPrepare
(
long
positionUs
)
throws
ExoPlaybackException
{
try
{
try
{
boolean
sourcePrepared
=
source
.
prepare
();
boolean
sourcePrepared
=
source
.
prepare
(
positionUs
);
if
(!
sourcePrepared
)
{
if
(!
sourcePrepared
)
{
return
TrackRenderer
.
STATE_UNPREPARED
;
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;
...
@@ -18,7 +18,24 @@ package com.google.android.exoplayer.upstream;
/**
/**
* Provides estimates of the currently available bandwidth.
* 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.
* 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;
...
@@ -26,25 +26,7 @@ import android.os.Handler;
* Counts transferred bytes while transfers are open and creates a bandwidth sample and updated
* Counts transferred bytes while transfers are open and creates a bandwidth sample and updated
* bandwidth estimate each time a transfer ends.
* bandwidth estimate each time a transfer ends.
*/
*/
public
class
DefaultBandwidthMeter
implements
BandwidthMeter
,
TransferListener
{
public
class
DefaultBandwidthMeter
implements
BandwidthMeter
{
/**
* 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
);
}
private
static
final
int
DEFAULT_MAX_WEIGHT
=
2000
;
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;
...
@@ -28,6 +28,8 @@ import java.io.IOException;
import
java.io.InputStream
;
import
java.io.InputStream
;
import
java.io.InterruptedIOException
;
import
java.io.InterruptedIOException
;
import
java.net.HttpURLConnection
;
import
java.net.HttpURLConnection
;
import
java.net.NoRouteToHostException
;
import
java.net.ProtocolException
;
import
java.net.URL
;
import
java.net.URL
;
import
java.util.HashMap
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.List
;
...
@@ -38,17 +40,30 @@ import java.util.regex.Pattern;
...
@@ -38,17 +40,30 @@ import java.util.regex.Pattern;
/**
/**
* A {@link HttpDataSource} that uses Android's {@link HttpURLConnection}.
* 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
{
public
class
DefaultHttpDataSource
implements
HttpDataSource
{
/**
* The default connection timeout, in milliseconds.
*/
public
static
final
int
DEFAULT_CONNECT_TIMEOUT_MILLIS
=
8
*
1000
;
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
;
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
String
TAG
=
"HttpDataSource"
;
private
static
final
Pattern
CONTENT_RANGE_HEADER
=
private
static
final
Pattern
CONTENT_RANGE_HEADER
=
Pattern
.
compile
(
"^bytes (\\d+)-(\\d+)/(\\d+)$"
);
Pattern
.
compile
(
"^bytes (\\d+)-(\\d+)/(\\d+)$"
);
private
static
final
AtomicReference
<
byte
[]>
skipBufferReference
=
new
AtomicReference
<
byte
[]>();
private
static
final
AtomicReference
<
byte
[]>
skipBufferReference
=
new
AtomicReference
<
byte
[]>();
private
final
boolean
allowCrossProtocolRedirects
;
private
final
int
connectTimeoutMillis
;
private
final
int
connectTimeoutMillis
;
private
final
int
readTimeoutMillis
;
private
final
int
readTimeoutMillis
;
private
final
String
userAgent
;
private
final
String
userAgent
;
...
@@ -103,12 +118,33 @@ public class DefaultHttpDataSource implements HttpDataSource {
...
@@ -103,12 +118,33 @@ public class DefaultHttpDataSource implements HttpDataSource {
*/
*/
public
DefaultHttpDataSource
(
String
userAgent
,
Predicate
<
String
>
contentTypePredicate
,
public
DefaultHttpDataSource
(
String
userAgent
,
Predicate
<
String
>
contentTypePredicate
,
TransferListener
listener
,
int
connectTimeoutMillis
,
int
readTimeoutMillis
)
{
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
.
userAgent
=
Assertions
.
checkNotEmpty
(
userAgent
);
this
.
contentTypePredicate
=
contentTypePredicate
;
this
.
contentTypePredicate
=
contentTypePredicate
;
this
.
listener
=
listener
;
this
.
listener
=
listener
;
this
.
requestProperties
=
new
HashMap
<
String
,
String
>();
this
.
requestProperties
=
new
HashMap
<
String
,
String
>();
this
.
connectTimeoutMillis
=
connectTimeoutMillis
;
this
.
connectTimeoutMillis
=
connectTimeoutMillis
;
this
.
readTimeoutMillis
=
readTimeoutMillis
;
this
.
readTimeoutMillis
=
readTimeoutMillis
;
this
.
allowCrossProtocolRedirects
=
allowCrossProtocolRedirects
;
}
}
@Override
@Override
...
@@ -283,8 +319,58 @@ public class DefaultHttpDataSource implements HttpDataSource {
...
@@ -283,8 +319,58 @@ public class DefaultHttpDataSource implements HttpDataSource {
return
bytesToRead
==
C
.
LENGTH_UNBOUNDED
?
bytesToRead
:
bytesToRead
-
bytesRead
;
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
{
private
HttpURLConnection
makeConnection
(
DataSpec
dataSpec
)
throws
IOException
{
URL
url
=
new
URL
(
dataSpec
.
uri
.
toString
());
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
();
HttpURLConnection
connection
=
(
HttpURLConnection
)
url
.
openConnection
();
connection
.
setConnectTimeout
(
connectTimeoutMillis
);
connection
.
setConnectTimeout
(
connectTimeoutMillis
);
connection
.
setReadTimeout
(
readTimeoutMillis
);
connection
.
setReadTimeout
(
readTimeoutMillis
);
...
@@ -294,28 +380,56 @@ public class DefaultHttpDataSource implements HttpDataSource {
...
@@ -294,28 +380,56 @@ public class DefaultHttpDataSource implements HttpDataSource {
connection
.
setRequestProperty
(
property
.
getKey
(),
property
.
getValue
());
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
);
connection
.
setRequestProperty
(
"User-Agent"
,
userAgent
);
if
(
(
dataSpec
.
flags
&
DataSpec
.
FLAG_ALLOW_GZIP
)
==
0
)
{
if
(
!
allowGzip
)
{
connection
.
setRequestProperty
(
"Accept-Encoding"
,
"identity"
);
connection
.
setRequestProperty
(
"Accept-Encoding"
,
"identity"
);
}
}
connection
.
connect
();
return
connection
;
return
connection
;
}
}
private
void
setRangeHeader
(
HttpURLConnection
connection
,
DataSpec
dataSpec
)
{
/**
if
(
dataSpec
.
position
==
0
&&
dataSpec
.
length
==
C
.
LENGTH_UNBOUNDED
)
{
* Handles a redirect.
// Not required.
*
return
;
* @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
+
"-"
;
// Form the new url.
if
(
dataSpec
.
length
!=
C
.
LENGTH_UNBOUNDED
)
{
URL
url
=
new
URL
(
originalUrl
,
location
);
rangeRequest
+=
(
dataSpec
.
position
+
dataSpec
.
length
-
1
);
// 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
;
long
contentLength
=
C
.
LENGTH_UNBOUNDED
;
String
contentLengthHeader
=
connection
.
getHeaderField
(
"Content-Length"
);
String
contentLengthHeader
=
connection
.
getHeaderField
(
"Content-Length"
);
if
(!
TextUtils
.
isEmpty
(
contentLengthHeader
))
{
if
(!
TextUtils
.
isEmpty
(
contentLengthHeader
))
{
...
@@ -429,6 +543,9 @@ public class DefaultHttpDataSource implements HttpDataSource {
...
@@ -429,6 +543,9 @@ public class DefaultHttpDataSource implements HttpDataSource {
return
read
;
return
read
;
}
}
/**
* Closes the current connection, if there is one.
*/
private
void
closeConnection
()
{
private
void
closeConnection
()
{
if
(
connection
!=
null
)
{
if
(
connection
!=
null
)
{
connection
.
disconnect
();
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 {
...
@@ -36,15 +36,36 @@ public final class DefaultUriDataSource implements UriDataSource {
private
UriDataSource
dataSource
;
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.
* {@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 userAgent The User-Agent string that should be used when requesting remote data.
* @param transferListener An optional listener.
* @param transferListener An optional listener.
*/
*/
public
DefaultUriDataSource
(
String
userAgent
,
TransferListener
transferListener
)
{
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
),
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 @@
...
@@ -15,6 +15,11 @@
*/
*/
package
com
.
google
.
android
.
exoplayer
.
util
;
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.
* Defines common MIME types and helper methods.
*/
*/
...
@@ -119,4 +124,37 @@ public class MimeTypes {
...
@@ -119,4 +124,37 @@ public class MimeTypes {
return
mimeType
.
equals
(
APPLICATION_TTML
);
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 @@
...
@@ -8,5 +8,5 @@
# project structure.
# project structure.
# Project target.
# Project target.
target
=
android-2
1
target
=
android-2
2
android.library
=
true
android.library
=
true
library/src/test/AndroidManifest.xml
View file @
dd41d14a
...
@@ -17,7 +17,7 @@
...
@@ -17,7 +17,7 @@
<manifest
xmlns:android=
"http://schemas.android.com/apk/res/android"
<manifest
xmlns:android=
"http://schemas.android.com/apk/res/android"
package=
"com.google.android.exoplayer.tests"
>
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>
<application>
<uses-library
android:name=
"android.test.runner"
/>
<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 {
...
@@ -579,7 +579,7 @@ public class Mp4ExtractorTest extends TestCase {
try
{
try
{
switch
(
message
.
what
)
{
switch
(
message
.
what
)
{
case
MSG_PREPARE:
case
MSG_PREPARE:
if
(!
source
.
prepare
())
{
if
(!
source
.
prepare
(
0
))
{
sendEmptyMessage
(
MSG_PREPARE
);
sendEmptyMessage
(
MSG_PREPARE
);
}
else
{
}
else
{
// Select the video track and get its metadata.
// Select the video track and get its metadata.
...
...
library/src/test/project.properties
View file @
dd41d14a
...
@@ -11,4 +11,4 @@
...
@@ -11,4 +11,4 @@
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
# Project target.
# 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