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
7f70db97
authored
Feb 25, 2015
by
ojw28
Browse files
Options
_('Browse Files')
Download
Plain Diff
Merge pull request #321 from google/dev
dev -> dev-webm-vp9-opus
parents
430d06d4
006986cc
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
593 additions
and
572 deletions
demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java
demo/src/main/java/com/google/android/exoplayer/demo/player/DashRendererBuilder.java
demo/src/main/java/com/google/android/exoplayer/demo/player/HlsRendererBuilder.java
demo/src/main/java/com/google/android/exoplayer/demo/player/SmoothStreamingRendererBuilder.java
library/src/main/java/com/google/android/exoplayer/chunk/ChunkSampleSource.java
library/src/main/java/com/google/android/exoplayer/dash/mpd/MediaPresentationDescriptionParser.java
library/src/main/java/com/google/android/exoplayer/dash/mpd/SegmentBase.java
library/src/main/java/com/google/android/exoplayer/dash/mpd/UtcTimingElementResolver.java
library/src/main/java/com/google/android/exoplayer/hls/HlsChunkSource.java
library/src/main/java/com/google/android/exoplayer/hls/HlsPlaylistParser.java
library/src/main/java/com/google/android/exoplayer/hls/HlsSampleSource.java
library/src/main/java/com/google/android/exoplayer/mp4/Mp4Util.java
library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingManifestParser.java
library/src/main/java/com/google/android/exoplayer/upstream/DataSourceInputStream.java
library/src/main/java/com/google/android/exoplayer/upstream/DefaultHttpDataSource.java
library/src/main/java/com/google/android/exoplayer/upstream/HttpDataSource.java
library/src/main/java/com/google/android/exoplayer/util/NetworkLoadable.java → library/src/main/java/com/google/android/exoplayer/upstream/NetworkLoadable.java
library/src/main/java/com/google/android/exoplayer/upstream/UriDataSource.java
library/src/main/java/com/google/android/exoplayer/util/ManifestFetcher.java
library/src/main/java/com/google/android/exoplayer/util/ManifestParser.java
demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java
View file @
7f70db97
...
@@ -206,13 +206,13 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
...
@@ -206,13 +206,13 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
String
userAgent
=
DemoUtil
.
getUserAgent
(
this
);
String
userAgent
=
DemoUtil
.
getUserAgent
(
this
);
switch
(
contentType
)
{
switch
(
contentType
)
{
case
DemoUtil
.
TYPE_SS
:
case
DemoUtil
.
TYPE_SS
:
return
new
SmoothStreamingRendererBuilder
(
userAgent
,
contentUri
.
toString
(),
contentId
,
return
new
SmoothStreamingRendererBuilder
(
userAgent
,
contentUri
.
toString
(),
new
SmoothStreamingTestMediaDrmCallback
(),
debugTextView
);
new
SmoothStreamingTestMediaDrmCallback
(),
debugTextView
);
case
DemoUtil
.
TYPE_DASH
:
case
DemoUtil
.
TYPE_DASH
:
return
new
DashRendererBuilder
(
userAgent
,
contentUri
.
toString
(),
contentId
,
return
new
DashRendererBuilder
(
userAgent
,
contentUri
.
toString
(),
new
WidevineTestMediaDrmCallback
(
contentId
),
debugTextView
,
audioCapabilities
);
new
WidevineTestMediaDrmCallback
(
contentId
),
debugTextView
,
audioCapabilities
);
case
DemoUtil
.
TYPE_HLS
:
case
DemoUtil
.
TYPE_HLS
:
return
new
HlsRendererBuilder
(
userAgent
,
contentUri
.
toString
()
,
contentId
);
return
new
HlsRendererBuilder
(
userAgent
,
contentUri
.
toString
());
default
:
default
:
return
new
DefaultRendererBuilder
(
this
,
contentUri
,
debugTextView
);
return
new
DefaultRendererBuilder
(
this
,
contentUri
,
debugTextView
);
}
}
...
...
demo/src/main/java/com/google/android/exoplayer/demo/player/DashRendererBuilder.java
View file @
7f70db97
...
@@ -52,6 +52,8 @@ import com.google.android.exoplayer.text.webvtt.WebvttParser;
...
@@ -52,6 +52,8 @@ import com.google.android.exoplayer.text.webvtt.WebvttParser;
import
com.google.android.exoplayer.upstream.BufferPool
;
import
com.google.android.exoplayer.upstream.BufferPool
;
import
com.google.android.exoplayer.upstream.DataSource
;
import
com.google.android.exoplayer.upstream.DataSource
;
import
com.google.android.exoplayer.upstream.DefaultBandwidthMeter
;
import
com.google.android.exoplayer.upstream.DefaultBandwidthMeter
;
import
com.google.android.exoplayer.upstream.DefaultHttpDataSource
;
import
com.google.android.exoplayer.upstream.HttpDataSource
;
import
com.google.android.exoplayer.upstream.UriDataSource
;
import
com.google.android.exoplayer.upstream.UriDataSource
;
import
com.google.android.exoplayer.util.ManifestFetcher
;
import
com.google.android.exoplayer.util.ManifestFetcher
;
import
com.google.android.exoplayer.util.ManifestFetcher.ManifestCallback
;
import
com.google.android.exoplayer.util.ManifestFetcher.ManifestCallback
;
...
@@ -93,7 +95,6 @@ public class DashRendererBuilder implements RendererBuilder,
...
@@ -93,7 +95,6 @@ public class DashRendererBuilder implements RendererBuilder,
private
final
String
userAgent
;
private
final
String
userAgent
;
private
final
String
url
;
private
final
String
url
;
private
final
String
contentId
;
private
final
MediaDrmCallback
drmCallback
;
private
final
MediaDrmCallback
drmCallback
;
private
final
TextView
debugTextView
;
private
final
TextView
debugTextView
;
private
final
AudioCapabilities
audioCapabilities
;
private
final
AudioCapabilities
audioCapabilities
;
...
@@ -101,15 +102,15 @@ public class DashRendererBuilder implements RendererBuilder,
...
@@ -101,15 +102,15 @@ public class DashRendererBuilder implements RendererBuilder,
private
DemoPlayer
player
;
private
DemoPlayer
player
;
private
RendererBuilderCallback
callback
;
private
RendererBuilderCallback
callback
;
private
ManifestFetcher
<
MediaPresentationDescription
>
manifestFetcher
;
private
ManifestFetcher
<
MediaPresentationDescription
>
manifestFetcher
;
private
HttpDataSource
manifestDataSource
;
private
MediaPresentationDescription
manifest
;
private
MediaPresentationDescription
manifest
;
private
long
elapsedRealtimeOffset
;
private
long
elapsedRealtimeOffset
;
public
DashRendererBuilder
(
String
userAgent
,
String
url
,
String
contentId
,
public
DashRendererBuilder
(
String
userAgent
,
String
url
,
MediaDrmCallback
drmCallback
,
MediaDrmCallback
drmCallback
,
TextView
debugTextView
,
AudioCapabilities
audioCapabilities
)
{
TextView
debugTextView
,
AudioCapabilities
audioCapabilities
)
{
this
.
userAgent
=
userAgent
;
this
.
userAgent
=
userAgent
;
this
.
url
=
url
;
this
.
url
=
url
;
this
.
contentId
=
contentId
;
this
.
drmCallback
=
drmCallback
;
this
.
drmCallback
=
drmCallback
;
this
.
debugTextView
=
debugTextView
;
this
.
debugTextView
=
debugTextView
;
this
.
audioCapabilities
=
audioCapabilities
;
this
.
audioCapabilities
=
audioCapabilities
;
...
@@ -120,16 +121,17 @@ public class DashRendererBuilder implements RendererBuilder,
...
@@ -120,16 +121,17 @@ public class DashRendererBuilder implements RendererBuilder,
this
.
player
=
player
;
this
.
player
=
player
;
this
.
callback
=
callback
;
this
.
callback
=
callback
;
MediaPresentationDescriptionParser
parser
=
new
MediaPresentationDescriptionParser
();
MediaPresentationDescriptionParser
parser
=
new
MediaPresentationDescriptionParser
();
manifestFetcher
=
new
ManifestFetcher
<
MediaPresentationDescription
>(
parser
,
contentId
,
url
,
manifestDataSource
=
new
DefaultHttpDataSource
(
userAgent
,
null
);
userAgent
);
manifestFetcher
=
new
ManifestFetcher
<
MediaPresentationDescription
>(
url
,
manifestDataSource
,
parser
);
manifestFetcher
.
singleLoad
(
player
.
getMainHandler
().
getLooper
(),
this
);
manifestFetcher
.
singleLoad
(
player
.
getMainHandler
().
getLooper
(),
this
);
}
}
@Override
@Override
public
void
on
Manifest
(
String
contentId
,
MediaPresentationDescription
manifest
)
{
public
void
on
SingleManifest
(
MediaPresentationDescription
manifest
)
{
this
.
manifest
=
manifest
;
this
.
manifest
=
manifest
;
if
(
manifest
.
dynamic
&&
manifest
.
utcTiming
!=
null
)
{
if
(
manifest
.
dynamic
&&
manifest
.
utcTiming
!=
null
)
{
UtcTimingElementResolver
.
resolveTimingElement
(
userAgent
,
manifest
.
utcTiming
,
UtcTimingElementResolver
.
resolveTimingElement
(
manifestDataSource
,
manifest
.
utcTiming
,
manifestFetcher
.
getManifestLoadTimestamp
(),
this
);
manifestFetcher
.
getManifestLoadTimestamp
(),
this
);
}
else
{
}
else
{
buildRenderers
();
buildRenderers
();
...
@@ -137,7 +139,7 @@ public class DashRendererBuilder implements RendererBuilder,
...
@@ -137,7 +139,7 @@ public class DashRendererBuilder implements RendererBuilder,
}
}
@Override
@Override
public
void
on
ManifestError
(
String
contentId
,
IOException
e
)
{
public
void
on
SingleManifestError
(
IOException
e
)
{
callback
.
onRenderersError
(
e
);
callback
.
onRenderersError
(
e
);
}
}
...
...
demo/src/main/java/com/google/android/exoplayer/demo/player/HlsRendererBuilder.java
View file @
7f70db97
...
@@ -29,6 +29,7 @@ import com.google.android.exoplayer.metadata.MetadataTrackRenderer;
...
@@ -29,6 +29,7 @@ import com.google.android.exoplayer.metadata.MetadataTrackRenderer;
import
com.google.android.exoplayer.text.eia608.Eia608TrackRenderer
;
import
com.google.android.exoplayer.text.eia608.Eia608TrackRenderer
;
import
com.google.android.exoplayer.upstream.DataSource
;
import
com.google.android.exoplayer.upstream.DataSource
;
import
com.google.android.exoplayer.upstream.DefaultBandwidthMeter
;
import
com.google.android.exoplayer.upstream.DefaultBandwidthMeter
;
import
com.google.android.exoplayer.upstream.DefaultHttpDataSource
;
import
com.google.android.exoplayer.upstream.UriDataSource
;
import
com.google.android.exoplayer.upstream.UriDataSource
;
import
com.google.android.exoplayer.util.ManifestFetcher
;
import
com.google.android.exoplayer.util.ManifestFetcher
;
import
com.google.android.exoplayer.util.ManifestFetcher.ManifestCallback
;
import
com.google.android.exoplayer.util.ManifestFetcher.ManifestCallback
;
...
@@ -45,15 +46,13 @@ public class HlsRendererBuilder implements RendererBuilder, ManifestCallback<Hls
...
@@ -45,15 +46,13 @@ 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
String
contentId
;
private
DemoPlayer
player
;
private
DemoPlayer
player
;
private
RendererBuilderCallback
callback
;
private
RendererBuilderCallback
callback
;
public
HlsRendererBuilder
(
String
userAgent
,
String
url
,
String
contentId
)
{
public
HlsRendererBuilder
(
String
userAgent
,
String
url
)
{
this
.
userAgent
=
userAgent
;
this
.
userAgent
=
userAgent
;
this
.
url
=
url
;
this
.
url
=
url
;
this
.
contentId
=
contentId
;
}
}
@Override
@Override
...
@@ -62,17 +61,17 @@ public class HlsRendererBuilder implements RendererBuilder, ManifestCallback<Hls
...
@@ -62,17 +61,17 @@ public class HlsRendererBuilder implements RendererBuilder, ManifestCallback<Hls
this
.
callback
=
callback
;
this
.
callback
=
callback
;
HlsPlaylistParser
parser
=
new
HlsPlaylistParser
();
HlsPlaylistParser
parser
=
new
HlsPlaylistParser
();
ManifestFetcher
<
HlsPlaylist
>
playlistFetcher
=
ManifestFetcher
<
HlsPlaylist
>
playlistFetcher
=
new
ManifestFetcher
<
HlsPlaylist
>(
parser
,
contentId
,
url
,
userAgent
);
new
ManifestFetcher
<
HlsPlaylist
>(
url
,
new
DefaultHttpDataSource
(
userAgent
,
null
),
parser
);
playlistFetcher
.
singleLoad
(
player
.
getMainHandler
().
getLooper
(),
this
);
playlistFetcher
.
singleLoad
(
player
.
getMainHandler
().
getLooper
(),
this
);
}
}
@Override
@Override
public
void
on
ManifestError
(
String
contentId
,
IOException
e
)
{
public
void
on
SingleManifestError
(
IOException
e
)
{
callback
.
onRenderersError
(
e
);
callback
.
onRenderersError
(
e
);
}
}
@Override
@Override
public
void
on
Manifest
(
String
contentId
,
HlsPlaylist
manifest
)
{
public
void
on
SingleManifest
(
HlsPlaylist
manifest
)
{
DefaultBandwidthMeter
bandwidthMeter
=
new
DefaultBandwidthMeter
();
DefaultBandwidthMeter
bandwidthMeter
=
new
DefaultBandwidthMeter
();
DataSource
dataSource
=
new
UriDataSource
(
userAgent
,
bandwidthMeter
);
DataSource
dataSource
=
new
UriDataSource
(
userAgent
,
bandwidthMeter
);
...
...
demo/src/main/java/com/google/android/exoplayer/demo/player/SmoothStreamingRendererBuilder.java
View file @
7f70db97
...
@@ -42,6 +42,7 @@ import com.google.android.exoplayer.text.ttml.TtmlParser;
...
@@ -42,6 +42,7 @@ import com.google.android.exoplayer.text.ttml.TtmlParser;
import
com.google.android.exoplayer.upstream.BufferPool
;
import
com.google.android.exoplayer.upstream.BufferPool
;
import
com.google.android.exoplayer.upstream.DataSource
;
import
com.google.android.exoplayer.upstream.DataSource
;
import
com.google.android.exoplayer.upstream.DefaultBandwidthMeter
;
import
com.google.android.exoplayer.upstream.DefaultBandwidthMeter
;
import
com.google.android.exoplayer.upstream.DefaultHttpDataSource
;
import
com.google.android.exoplayer.upstream.UriDataSource
;
import
com.google.android.exoplayer.upstream.UriDataSource
;
import
com.google.android.exoplayer.util.ManifestFetcher
;
import
com.google.android.exoplayer.util.ManifestFetcher
;
import
com.google.android.exoplayer.util.Util
;
import
com.google.android.exoplayer.util.Util
;
...
@@ -70,7 +71,6 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
...
@@ -70,7 +71,6 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
private
final
String
userAgent
;
private
final
String
userAgent
;
private
final
String
url
;
private
final
String
url
;
private
final
String
contentId
;
private
final
MediaDrmCallback
drmCallback
;
private
final
MediaDrmCallback
drmCallback
;
private
final
TextView
debugTextView
;
private
final
TextView
debugTextView
;
...
@@ -78,11 +78,10 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
...
@@ -78,11 +78,10 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
private
RendererBuilderCallback
callback
;
private
RendererBuilderCallback
callback
;
private
ManifestFetcher
<
SmoothStreamingManifest
>
manifestFetcher
;
private
ManifestFetcher
<
SmoothStreamingManifest
>
manifestFetcher
;
public
SmoothStreamingRendererBuilder
(
String
userAgent
,
String
url
,
String
contentId
,
public
SmoothStreamingRendererBuilder
(
String
userAgent
,
String
url
,
MediaDrmCallback
drmCallback
,
MediaDrmCallback
drmCallback
,
TextView
debugTextView
)
{
TextView
debugTextView
)
{
this
.
userAgent
=
userAgent
;
this
.
userAgent
=
userAgent
;
this
.
url
=
url
;
this
.
url
=
url
;
this
.
contentId
=
contentId
;
this
.
drmCallback
=
drmCallback
;
this
.
drmCallback
=
drmCallback
;
this
.
debugTextView
=
debugTextView
;
this
.
debugTextView
=
debugTextView
;
}
}
...
@@ -92,18 +91,18 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
...
@@ -92,18 +91,18 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
this
.
player
=
player
;
this
.
player
=
player
;
this
.
callback
=
callback
;
this
.
callback
=
callback
;
SmoothStreamingManifestParser
parser
=
new
SmoothStreamingManifestParser
();
SmoothStreamingManifestParser
parser
=
new
SmoothStreamingManifestParser
();
manifestFetcher
=
new
ManifestFetcher
<
SmoothStreamingManifest
>(
parser
,
contentId
,
manifestFetcher
=
new
ManifestFetcher
<
SmoothStreamingManifest
>(
url
+
"/Manifest"
,
url
+
"/Manifest"
,
userAgent
);
new
DefaultHttpDataSource
(
userAgent
,
null
),
parser
);
manifestFetcher
.
singleLoad
(
player
.
getMainHandler
().
getLooper
(),
this
);
manifestFetcher
.
singleLoad
(
player
.
getMainHandler
().
getLooper
(),
this
);
}
}
@Override
@Override
public
void
on
ManifestError
(
String
contentId
,
IOException
exception
)
{
public
void
on
SingleManifestError
(
IOException
exception
)
{
callback
.
onRenderersError
(
exception
);
callback
.
onRenderersError
(
exception
);
}
}
@Override
@Override
public
void
on
Manifest
(
String
contentId
,
SmoothStreamingManifest
manifest
)
{
public
void
on
SingleManifest
(
SmoothStreamingManifest
manifest
)
{
Handler
mainHandler
=
player
.
getMainHandler
();
Handler
mainHandler
=
player
.
getMainHandler
();
LoadControl
loadControl
=
new
DefaultLoadControl
(
new
BufferPool
(
BUFFER_SEGMENT_SIZE
));
LoadControl
loadControl
=
new
DefaultLoadControl
(
new
BufferPool
(
BUFFER_SEGMENT_SIZE
));
DefaultBandwidthMeter
bandwidthMeter
=
new
DefaultBandwidthMeter
(
mainHandler
,
player
);
DefaultBandwidthMeter
bandwidthMeter
=
new
DefaultBandwidthMeter
(
mainHandler
,
player
);
...
...
library/src/main/java/com/google/android/exoplayer/chunk/ChunkSampleSource.java
View file @
7f70db97
...
@@ -276,13 +276,14 @@ public class ChunkSampleSource implements SampleSource, Loader.Callback {
...
@@ -276,13 +276,14 @@ public class ChunkSampleSource implements SampleSource, Loader.Callback {
boolean
haveSamples
=
false
;
boolean
haveSamples
=
false
;
if
(
isPendingReset
()
||
mediaChunks
.
isEmpty
())
{
if
(
isPendingReset
()
||
mediaChunks
.
isEmpty
())
{
// No sample available.
// No sample available.
}
else
if
(
mediaChunks
.
getFirst
().
sampleAvailable
(
))
{
}
else
if
(
sampleAvailableOrFinishedLastChunk
(
mediaChunks
.
getFirst
()
))
{
// There's a sample available to be read from the current chunk.
// There's a sample available to be read from the current chunk.
haveSamples
=
true
;
haveSamples
=
true
;
}
else
{
}
else
{
// It may be the case that the current chunk has been fully read but not yet discarded and
// It may be the case that the current chunk has been fully read but not yet discarded and
// that the next chunk has an available sample. Return true if so, otherwise false.
// that the next chunk has an available sample. Return true if so, otherwise false.
haveSamples
=
mediaChunks
.
size
()
>
1
&&
mediaChunks
.
get
(
1
).
sampleAvailable
();
haveSamples
=
mediaChunks
.
size
()
>
1
&&
sampleAvailableOrFinishedLastChunk
(
mediaChunks
.
get
(
1
));
}
}
if
(!
haveSamples
)
{
if
(!
haveSamples
)
{
...
@@ -716,6 +717,10 @@ public class ChunkSampleSource implements SampleSource, Loader.Callback {
...
@@ -716,6 +717,10 @@ public class ChunkSampleSource implements SampleSource, Loader.Callback {
return
true
;
return
true
;
}
}
private
boolean
sampleAvailableOrFinishedLastChunk
(
MediaChunk
chunk
)
throws
IOException
{
return
chunk
.
sampleAvailable
()
||
(
chunk
.
isLastChunk
()
&&
chunk
.
isReadFinished
());
}
private
boolean
isMediaChunk
(
Chunk
chunk
)
{
private
boolean
isMediaChunk
(
Chunk
chunk
)
{
return
chunk
instanceof
MediaChunk
;
return
chunk
instanceof
MediaChunk
;
}
}
...
...
library/src/main/java/com/google/android/exoplayer/dash/mpd/MediaPresentationDescriptionParser.java
View file @
7f70db97
...
@@ -21,8 +21,8 @@ import com.google.android.exoplayer.dash.mpd.SegmentBase.SegmentList;
...
@@ -21,8 +21,8 @@ import com.google.android.exoplayer.dash.mpd.SegmentBase.SegmentList;
import
com.google.android.exoplayer.dash.mpd.SegmentBase.SegmentTemplate
;
import
com.google.android.exoplayer.dash.mpd.SegmentBase.SegmentTemplate
;
import
com.google.android.exoplayer.dash.mpd.SegmentBase.SegmentTimelineElement
;
import
com.google.android.exoplayer.dash.mpd.SegmentBase.SegmentTimelineElement
;
import
com.google.android.exoplayer.dash.mpd.SegmentBase.SingleSegmentBase
;
import
com.google.android.exoplayer.dash.mpd.SegmentBase.SingleSegmentBase
;
import
com.google.android.exoplayer.upstream.NetworkLoadable
;
import
com.google.android.exoplayer.util.Assertions
;
import
com.google.android.exoplayer.util.Assertions
;
import
com.google.android.exoplayer.util.ManifestParser
;
import
com.google.android.exoplayer.util.MimeTypes
;
import
com.google.android.exoplayer.util.MimeTypes
;
import
com.google.android.exoplayer.util.Util
;
import
com.google.android.exoplayer.util.Util
;
...
@@ -44,11 +44,25 @@ import java.util.List;
...
@@ -44,11 +44,25 @@ import java.util.List;
* A parser of media presentation description files.
* A parser of media presentation description files.
*/
*/
public
class
MediaPresentationDescriptionParser
extends
DefaultHandler
public
class
MediaPresentationDescriptionParser
extends
DefaultHandler
implements
Manifest
Parser
<
MediaPresentationDescription
>
{
implements
NetworkLoadable
.
Parser
<
MediaPresentationDescription
>
{
private
final
String
contentId
;
private
final
XmlPullParserFactory
xmlParserFactory
;
private
final
XmlPullParserFactory
xmlParserFactory
;
/**
* Equivalent to calling {@code new MediaPresentationDescriptionParser(null)}.
*/
public
MediaPresentationDescriptionParser
()
{
public
MediaPresentationDescriptionParser
()
{
this
(
null
);
}
/**
* @param contentId An optional content identifier to include in the parsed manifest.
*/
// TODO: Remove the need to inject a content identifier here, by not including it in the parsed
// manifest. Instead, it should be injected directly where needed (i.e. DashChunkSource).
public
MediaPresentationDescriptionParser
(
String
contentId
)
{
this
.
contentId
=
contentId
;
try
{
try
{
xmlParserFactory
=
XmlPullParserFactory
.
newInstance
();
xmlParserFactory
=
XmlPullParserFactory
.
newInstance
();
}
catch
(
XmlPullParserException
e
)
{
}
catch
(
XmlPullParserException
e
)
{
...
@@ -59,17 +73,17 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
...
@@ -59,17 +73,17 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
// MPD parsing.
// MPD parsing.
@Override
@Override
public
MediaPresentationDescription
parse
(
InputStream
inputStream
,
String
inputEncoding
,
public
MediaPresentationDescription
parse
(
String
connectionUrl
,
InputStream
inputStream
)
String
contentId
,
Uri
baseUrl
)
throws
IOException
,
ParserException
{
throws
IOException
,
ParserException
{
try
{
try
{
XmlPullParser
xpp
=
xmlParserFactory
.
newPullParser
();
XmlPullParser
xpp
=
xmlParserFactory
.
newPullParser
();
xpp
.
setInput
(
inputStream
,
inputEncoding
);
xpp
.
setInput
(
inputStream
,
null
);
int
eventType
=
xpp
.
next
();
int
eventType
=
xpp
.
next
();
if
(
eventType
!=
XmlPullParser
.
START_TAG
||
!
"MPD"
.
equals
(
xpp
.
getName
()))
{
if
(
eventType
!=
XmlPullParser
.
START_TAG
||
!
"MPD"
.
equals
(
xpp
.
getName
()))
{
throw
new
ParserException
(
throw
new
ParserException
(
"inputStream does not contain a valid media presentation description"
);
"inputStream does not contain a valid media presentation description"
);
}
}
return
parseMediaPresentationDescription
(
xpp
,
contentId
,
baseUrl
);
return
parseMediaPresentationDescription
(
xpp
,
Util
.
parseBaseUri
(
connectionUrl
)
);
}
catch
(
XmlPullParserException
e
)
{
}
catch
(
XmlPullParserException
e
)
{
throw
new
ParserException
(
e
);
throw
new
ParserException
(
e
);
}
catch
(
ParseException
e
)
{
}
catch
(
ParseException
e
)
{
...
@@ -78,7 +92,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
...
@@ -78,7 +92,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
}
}
protected
MediaPresentationDescription
parseMediaPresentationDescription
(
XmlPullParser
xpp
,
protected
MediaPresentationDescription
parseMediaPresentationDescription
(
XmlPullParser
xpp
,
String
contentId
,
Uri
baseUrl
)
throws
XmlPullParserException
,
IOException
,
ParseException
{
Uri
baseUrl
)
throws
XmlPullParserException
,
IOException
,
ParseException
{
long
availabilityStartTime
=
parseDateTime
(
xpp
,
"availabilityStartTime"
,
-
1
);
long
availabilityStartTime
=
parseDateTime
(
xpp
,
"availabilityStartTime"
,
-
1
);
long
durationMs
=
parseDuration
(
xpp
,
"mediaPresentationDuration"
,
-
1
);
long
durationMs
=
parseDuration
(
xpp
,
"mediaPresentationDuration"
,
-
1
);
long
minBufferTimeMs
=
parseDuration
(
xpp
,
"minBufferTime"
,
-
1
);
long
minBufferTimeMs
=
parseDuration
(
xpp
,
"minBufferTime"
,
-
1
);
...
@@ -97,7 +111,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
...
@@ -97,7 +111,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
}
else
if
(
isStartTag
(
xpp
,
"UTCTiming"
))
{
}
else
if
(
isStartTag
(
xpp
,
"UTCTiming"
))
{
utcTiming
=
parseUtcTiming
(
xpp
);
utcTiming
=
parseUtcTiming
(
xpp
);
}
else
if
(
isStartTag
(
xpp
,
"Period"
))
{
}
else
if
(
isStartTag
(
xpp
,
"Period"
))
{
periods
.
add
(
parsePeriod
(
xpp
,
contentId
,
baseUrl
,
durationMs
));
periods
.
add
(
parsePeriod
(
xpp
,
baseUrl
,
durationMs
));
}
}
}
while
(!
isEndTag
(
xpp
,
"MPD"
));
}
while
(!
isEndTag
(
xpp
,
"MPD"
));
...
@@ -123,7 +137,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
...
@@ -123,7 +137,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
return
new
UtcTimingElement
(
schemeIdUri
,
value
);
return
new
UtcTimingElement
(
schemeIdUri
,
value
);
}
}
protected
Period
parsePeriod
(
XmlPullParser
xpp
,
String
contentId
,
Uri
baseUrl
,
long
mpdDurationMs
)
protected
Period
parsePeriod
(
XmlPullParser
xpp
,
Uri
baseUrl
,
long
mpdDurationMs
)
throws
XmlPullParserException
,
IOException
{
throws
XmlPullParserException
,
IOException
{
String
id
=
xpp
.
getAttributeValue
(
null
,
"id"
);
String
id
=
xpp
.
getAttributeValue
(
null
,
"id"
);
long
startMs
=
parseDuration
(
xpp
,
"start"
,
0
);
long
startMs
=
parseDuration
(
xpp
,
"start"
,
0
);
...
@@ -135,7 +149,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
...
@@ -135,7 +149,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
if
(
isStartTag
(
xpp
,
"BaseURL"
))
{
if
(
isStartTag
(
xpp
,
"BaseURL"
))
{
baseUrl
=
parseBaseUrl
(
xpp
,
baseUrl
);
baseUrl
=
parseBaseUrl
(
xpp
,
baseUrl
);
}
else
if
(
isStartTag
(
xpp
,
"AdaptationSet"
))
{
}
else
if
(
isStartTag
(
xpp
,
"AdaptationSet"
))
{
adaptationSets
.
add
(
parseAdaptationSet
(
xpp
,
contentId
,
baseUrl
,
startMs
,
durationMs
,
adaptationSets
.
add
(
parseAdaptationSet
(
xpp
,
baseUrl
,
startMs
,
durationMs
,
segmentBase
));
segmentBase
));
}
else
if
(
isStartTag
(
xpp
,
"SegmentBase"
))
{
}
else
if
(
isStartTag
(
xpp
,
"SegmentBase"
))
{
segmentBase
=
parseSegmentBase
(
xpp
,
baseUrl
,
null
);
segmentBase
=
parseSegmentBase
(
xpp
,
baseUrl
,
null
);
...
@@ -156,9 +170,8 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
...
@@ -156,9 +170,8 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
// AdaptationSet parsing.
// AdaptationSet parsing.
protected
AdaptationSet
parseAdaptationSet
(
XmlPullParser
xpp
,
String
contentId
,
Uri
baseUrl
,
protected
AdaptationSet
parseAdaptationSet
(
XmlPullParser
xpp
,
Uri
baseUrl
,
long
periodStartMs
,
long
periodStartMs
,
long
periodDurationMs
,
SegmentBase
segmentBase
)
long
periodDurationMs
,
SegmentBase
segmentBase
)
throws
XmlPullParserException
,
IOException
{
throws
XmlPullParserException
,
IOException
{
String
mimeType
=
xpp
.
getAttributeValue
(
null
,
"mimeType"
);
String
mimeType
=
xpp
.
getAttributeValue
(
null
,
"mimeType"
);
String
language
=
xpp
.
getAttributeValue
(
null
,
"lang"
);
String
language
=
xpp
.
getAttributeValue
(
null
,
"lang"
);
...
@@ -181,7 +194,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
...
@@ -181,7 +194,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
contentType
=
checkAdaptationSetTypeConsistency
(
contentType
,
contentType
=
checkAdaptationSetTypeConsistency
(
contentType
,
parseAdaptationSetType
(
xpp
.
getAttributeValue
(
null
,
"contentType"
)));
parseAdaptationSetType
(
xpp
.
getAttributeValue
(
null
,
"contentType"
)));
}
else
if
(
isStartTag
(
xpp
,
"Representation"
))
{
}
else
if
(
isStartTag
(
xpp
,
"Representation"
))
{
Representation
representation
=
parseRepresentation
(
xpp
,
contentId
,
baseUrl
,
periodStartMs
,
Representation
representation
=
parseRepresentation
(
xpp
,
baseUrl
,
periodStartMs
,
periodDurationMs
,
mimeType
,
language
,
segmentBase
);
periodDurationMs
,
mimeType
,
language
,
segmentBase
);
contentType
=
checkAdaptationSetTypeConsistency
(
contentType
,
contentType
=
checkAdaptationSetTypeConsistency
(
contentType
,
parseAdaptationSetTypeFromMimeType
(
representation
.
format
.
mimeType
));
parseAdaptationSetTypeFromMimeType
(
representation
.
format
.
mimeType
));
...
@@ -274,9 +287,9 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
...
@@ -274,9 +287,9 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
// Representation parsing.
// Representation parsing.
protected
Representation
parseRepresentation
(
XmlPullParser
xpp
,
String
contentId
,
Uri
baseUrl
,
protected
Representation
parseRepresentation
(
XmlPullParser
xpp
,
Uri
baseUrl
,
long
periodStartMs
,
long
period
StartMs
,
long
periodDurationMs
,
String
mimeType
,
String
language
,
long
period
DurationMs
,
String
mimeType
,
String
language
,
SegmentBase
segmentBase
)
SegmentBase
segmentBase
)
throws
XmlPullParserException
,
IOException
{
throws
XmlPullParserException
,
IOException
{
String
id
=
xpp
.
getAttributeValue
(
null
,
"id"
);
String
id
=
xpp
.
getAttributeValue
(
null
,
"id"
);
int
bandwidth
=
parseInt
(
xpp
,
"bandwidth"
);
int
bandwidth
=
parseInt
(
xpp
,
"bandwidth"
);
int
audioSamplingRate
=
parseInt
(
xpp
,
"audioSamplingRate"
);
int
audioSamplingRate
=
parseInt
(
xpp
,
"audioSamplingRate"
);
...
...
library/src/main/java/com/google/android/exoplayer/dash/mpd/SegmentBase.java
View file @
7f70db97
...
@@ -148,7 +148,8 @@ public abstract class SegmentBase {
...
@@ -148,7 +148,8 @@ public abstract class SegmentBase {
* @see DashSegmentIndex#getSegmentNum(long)
* @see DashSegmentIndex#getSegmentNum(long)
*/
*/
public
int
getSegmentNum
(
long
timeUs
)
{
public
int
getSegmentNum
(
long
timeUs
)
{
int
lowIndex
=
getFirstSegmentNum
();
final
int
firstSegmentNum
=
getFirstSegmentNum
();
int
lowIndex
=
firstSegmentNum
;
int
highIndex
=
getLastSegmentNum
();
int
highIndex
=
getLastSegmentNum
();
if
(
segmentTimeline
==
null
)
{
if
(
segmentTimeline
==
null
)
{
// All segments are of equal duration (with the possible exception of the last one).
// All segments are of equal duration (with the possible exception of the last one).
...
@@ -171,7 +172,7 @@ public abstract class SegmentBase {
...
@@ -171,7 +172,7 @@ public abstract class SegmentBase {
return
midIndex
;
return
midIndex
;
}
}
}
}
return
lowIndex
-
1
;
return
lowIndex
==
firstSegmentNum
?
lowIndex
:
highIndex
;
}
}
}
}
...
...
library/src/main/java/com/google/android/exoplayer/dash/mpd/UtcTimingElementResolver.java
View file @
7f70db97
...
@@ -16,10 +16,11 @@
...
@@ -16,10 +16,11 @@
package
com
.
google
.
android
.
exoplayer
.
dash
.
mpd
;
package
com
.
google
.
android
.
exoplayer
.
dash
.
mpd
;
import
com.google.android.exoplayer.ParserException
;
import
com.google.android.exoplayer.ParserException
;
import
com.google.android.exoplayer.upstream.HttpDataSource
;
import
com.google.android.exoplayer.upstream.Loader
;
import
com.google.android.exoplayer.upstream.Loader
;
import
com.google.android.exoplayer.upstream.Loader.Loadable
;
import
com.google.android.exoplayer.upstream.Loader.Loadable
;
import
com.google.android.exoplayer.upstream.NetworkLoadable
;
import
com.google.android.exoplayer.util.Assertions
;
import
com.google.android.exoplayer.util.Assertions
;
import
com.google.android.exoplayer.util.NetworkLoadable
;
import
com.google.android.exoplayer.util.Util
;
import
com.google.android.exoplayer.util.Util
;
import
android.os.SystemClock
;
import
android.os.SystemClock
;
...
@@ -62,36 +63,34 @@ public class UtcTimingElementResolver implements Loader.Callback {
...
@@ -62,36 +63,34 @@ public class UtcTimingElementResolver implements Loader.Callback {
void
onTimestampError
(
UtcTimingElement
utcTiming
,
IOException
e
);
void
onTimestampError
(
UtcTimingElement
utcTiming
,
IOException
e
);
}
}
private
static
final
int
TYPE_XS
=
0
;
private
final
HttpDataSource
httpDataSource
;
private
static
final
int
TYPE_ISO
=
1
;
private
final
String
userAgent
;
private
final
UtcTimingElement
timingElement
;
private
final
UtcTimingElement
timingElement
;
private
final
long
timingElementElapsedRealtime
;
private
final
long
timingElementElapsedRealtime
;
private
final
UtcTimingCallback
callback
;
private
final
UtcTimingCallback
callback
;
private
Loader
singleUseLoader
;
private
Loader
singleUseLoader
;
private
HttpTimestampLoadable
singleUseLoadable
;
private
NetworkLoadable
<
Long
>
singleUseLoadable
;
/**
/**
* Resolves a {@link UtcTimingElement}.
* Resolves a {@link UtcTimingElement}.
*
*
* @param
userAgent A user agent
to use should network requests be necessary.
* @param
httpDataSource A source
to use should network requests be necessary.
* @param timingElement The element to resolve.
* @param timingElement The element to resolve.
* @param timingElementElapsedRealtime The {@link SystemClock#elapsedRealtime()} timestamp at
* @param timingElementElapsedRealtime The {@link SystemClock#elapsedRealtime()} timestamp at
* which the element was obtained. Used if the element contains a timestamp directly.
* which the element was obtained. Used if the element contains a timestamp directly.
* @param callback The callback to invoke on resolution or failure.
* @param callback The callback to invoke on resolution or failure.
*/
*/
public
static
void
resolveTimingElement
(
String
userAgent
,
UtcTimingElement
timingElement
,
public
static
void
resolveTimingElement
(
HttpDataSource
httpDataSource
,
long
timingElementElapsedRealtime
,
UtcTimingCallback
callback
)
{
UtcTimingElement
timingElement
,
long
timingElementElapsedRealtime
,
UtcTimingElementResolver
resolver
=
new
UtcTimingElementResolver
(
userAgent
,
timingElement
,
UtcTimingCallback
callback
)
{
UtcTimingElementResolver
resolver
=
new
UtcTimingElementResolver
(
httpDataSource
,
timingElement
,
timingElementElapsedRealtime
,
callback
);
timingElementElapsedRealtime
,
callback
);
resolver
.
resolve
();
resolver
.
resolve
();
}
}
private
UtcTimingElementResolver
(
String
userAgent
,
UtcTimingElement
timingElement
,
private
UtcTimingElementResolver
(
HttpDataSource
httpDataSource
,
UtcTimingElement
timingElement
,
long
timingElementElapsedRealtime
,
UtcTimingCallback
callback
)
{
long
timingElementElapsedRealtime
,
UtcTimingCallback
callback
)
{
this
.
userAgent
=
userAgent
;
this
.
httpDataSource
=
httpDataSource
;
this
.
timingElement
=
Assertions
.
checkNotNull
(
timingElement
);
this
.
timingElement
=
Assertions
.
checkNotNull
(
timingElement
);
this
.
timingElementElapsedRealtime
=
timingElementElapsedRealtime
;
this
.
timingElementElapsedRealtime
=
timingElementElapsedRealtime
;
this
.
callback
=
Assertions
.
checkNotNull
(
callback
);
this
.
callback
=
Assertions
.
checkNotNull
(
callback
);
...
@@ -102,10 +101,10 @@ public class UtcTimingElementResolver implements Loader.Callback {
...
@@ -102,10 +101,10 @@ public class UtcTimingElementResolver implements Loader.Callback {
if
(
Util
.
areEqual
(
scheme
,
"urn:mpeg:dash:utc:direct:2012"
))
{
if
(
Util
.
areEqual
(
scheme
,
"urn:mpeg:dash:utc:direct:2012"
))
{
resolveDirect
();
resolveDirect
();
}
else
if
(
Util
.
areEqual
(
scheme
,
"urn:mpeg:dash:utc:http-iso:2014"
))
{
}
else
if
(
Util
.
areEqual
(
scheme
,
"urn:mpeg:dash:utc:http-iso:2014"
))
{
resolveHttp
(
TYPE_ISO
);
resolveHttp
(
new
Iso8601Parser
()
);
}
else
if
(
Util
.
areEqual
(
scheme
,
"urn:mpeg:dash:utc:http-xsdate:2012"
)
}
else
if
(
Util
.
areEqual
(
scheme
,
"urn:mpeg:dash:utc:http-xsdate:2012"
)
||
Util
.
areEqual
(
scheme
,
"urn:mpeg:dash:utc:http-xsdate:2014"
))
{
||
Util
.
areEqual
(
scheme
,
"urn:mpeg:dash:utc:http-xsdate:2014"
))
{
resolveHttp
(
TYPE_XS
);
resolveHttp
(
new
XsDateTimeParser
()
);
}
else
{
}
else
{
// Unsupported scheme.
// Unsupported scheme.
callback
.
onTimestampError
(
timingElement
,
new
IOException
(
"Unsupported utc timing scheme"
));
callback
.
onTimestampError
(
timingElement
,
new
IOException
(
"Unsupported utc timing scheme"
));
...
@@ -122,9 +121,9 @@ public class UtcTimingElementResolver implements Loader.Callback {
...
@@ -122,9 +121,9 @@ public class UtcTimingElementResolver implements Loader.Callback {
}
}
}
}
private
void
resolveHttp
(
int
type
)
{
private
void
resolveHttp
(
NetworkLoadable
.
Parser
<
Long
>
parser
)
{
singleUseLoader
=
new
Loader
(
"utctiming"
);
singleUseLoader
=
new
Loader
(
"utctiming"
);
singleUseLoadable
=
new
HttpTimestampLoadable
(
timingElement
.
value
,
userAgent
,
type
);
singleUseLoadable
=
new
NetworkLoadable
<
Long
>(
timingElement
.
value
,
httpDataSource
,
parser
);
singleUseLoader
.
startLoading
(
singleUseLoadable
,
this
);
singleUseLoader
.
startLoading
(
singleUseLoadable
,
this
);
}
}
...
@@ -150,32 +149,31 @@ public class UtcTimingElementResolver implements Loader.Callback {
...
@@ -150,32 +149,31 @@ public class UtcTimingElementResolver implements Loader.Callback {
singleUseLoader
.
release
();
singleUseLoader
.
release
();
}
}
private
static
class
HttpTimestampLoadable
extends
NetworkLoadable
<
Long
>
{
private
static
class
XsDateTimeParser
implements
NetworkLoadable
.
Parser
<
Long
>
{
private
final
int
type
;
public
HttpTimestampLoadable
(
String
url
,
String
userAgent
,
int
type
)
{
@Override
super
(
url
,
userAgent
);
public
Long
parse
(
String
connectionUrl
,
InputStream
inputStream
)
throws
ParserException
,
this
.
type
=
type
;
IOException
{
String
firstLine
=
new
BufferedReader
(
new
InputStreamReader
(
inputStream
)).
readLine
();
try
{
return
Util
.
parseXsDateTime
(
firstLine
);
}
catch
(
ParseException
e
)
{
throw
new
ParserException
(
e
);
}
}
}
}
private
static
class
Iso8601Parser
implements
NetworkLoadable
.
Parser
<
Long
>
{
@Override
@Override
protected
Long
parse
(
String
connectionUrl
,
InputStream
inputStream
,
String
inputEncoding
)
public
Long
parse
(
String
connectionUrl
,
InputStream
inputStream
)
throws
ParserException
,
throws
ParserException
,
IOException
{
IOException
{
BufferedReader
reader
=
new
BufferedReader
(
new
InputStreamReader
(
inputStream
));
String
firstLine
=
new
BufferedReader
(
new
InputStreamReader
(
inputStream
)).
readLine
();
String
firstLine
=
reader
.
readLine
();
try
{
try
{
switch
(
type
)
{
// TODO: It may be necessary to handle timestamp offsets from UTC.
case
TYPE_XS:
SimpleDateFormat
format
=
new
SimpleDateFormat
(
"yyyy-MM-dd'T'HH:mm:ss'Z'"
,
Locale
.
US
);
return
Util
.
parseXsDateTime
(
firstLine
);
return
format
.
parse
(
firstLine
).
getTime
();
case
TYPE_ISO:
// TODO: It may be necessary to handle timestamp offsets from UTC.
SimpleDateFormat
format
=
new
SimpleDateFormat
(
"yyyy-MM-dd'T'HH:mm:ss'Z'"
,
Locale
.
US
);
return
format
.
parse
(
firstLine
).
getTime
();
default
:
// Never happens.
throw
new
RuntimeException
();
}
}
catch
(
ParseException
e
)
{
}
catch
(
ParseException
e
)
{
throw
new
ParserException
(
e
);
throw
new
ParserException
(
e
);
}
}
...
...
library/src/main/java/com/google/android/exoplayer/hls/HlsChunkSource.java
View file @
7f70db97
...
@@ -433,8 +433,8 @@ public class HlsChunkSource {
...
@@ -433,8 +433,8 @@ public class HlsChunkSource {
private
MediaPlaylistChunk
newMediaPlaylistChunk
(
int
variantIndex
)
{
private
MediaPlaylistChunk
newMediaPlaylistChunk
(
int
variantIndex
)
{
Uri
mediaPlaylistUri
=
Util
.
getMergedUri
(
baseUri
,
enabledVariants
[
variantIndex
].
url
);
Uri
mediaPlaylistUri
=
Util
.
getMergedUri
(
baseUri
,
enabledVariants
[
variantIndex
].
url
);
DataSpec
dataSpec
=
new
DataSpec
(
mediaPlaylistUri
,
0
,
C
.
LENGTH_UNBOUNDED
,
null
);
DataSpec
dataSpec
=
new
DataSpec
(
mediaPlaylistUri
,
0
,
C
.
LENGTH_UNBOUNDED
,
null
);
Uri
baseUri
=
Util
.
parseBaseUri
(
mediaPlaylistUri
.
toString
());
return
new
MediaPlaylistChunk
(
variantIndex
,
upstreamDataSource
,
dataSpec
,
return
new
MediaPlaylistChunk
(
variantIndex
,
upstreamDataSource
,
dataSpec
,
baseUri
);
mediaPlaylistUri
.
toString
()
);
}
}
private
EncryptionKeyChunk
newEncryptionKeyChunk
(
Uri
keyUri
,
String
iv
)
{
private
EncryptionKeyChunk
newEncryptionKeyChunk
(
Uri
keyUri
,
String
iv
)
{
...
@@ -546,19 +546,19 @@ public class HlsChunkSource {
...
@@ -546,19 +546,19 @@ public class HlsChunkSource {
@SuppressWarnings
(
"hiding"
)
@SuppressWarnings
(
"hiding"
)
/* package */
final
int
variantIndex
;
/* package */
final
int
variantIndex
;
private
final
Uri
playlistBaseUri
;
private
final
String
playlistUrl
;
public
MediaPlaylistChunk
(
int
variantIndex
,
DataSource
dataSource
,
DataSpec
dataSpec
,
public
MediaPlaylistChunk
(
int
variantIndex
,
DataSource
dataSource
,
DataSpec
dataSpec
,
Uri
playlistBaseUri
)
{
String
playlistUrl
)
{
super
(
dataSource
,
dataSpec
,
scratchSpace
);
super
(
dataSource
,
dataSpec
,
scratchSpace
);
this
.
variantIndex
=
variantIndex
;
this
.
variantIndex
=
variantIndex
;
this
.
playlist
BaseUri
=
playlistBaseUri
;
this
.
playlist
Url
=
playlistUrl
;
}
}
@Override
@Override
protected
void
consume
(
byte
[]
data
,
int
limit
)
throws
IOException
{
protected
void
consume
(
byte
[]
data
,
int
limit
)
throws
IOException
{
HlsPlaylist
playlist
=
playlistParser
.
parse
(
new
ByteArrayInputStream
(
data
,
0
,
limit
)
,
HlsPlaylist
playlist
=
playlistParser
.
parse
(
playlistUrl
,
n
ull
,
null
,
playlistBaseUri
);
n
ew
ByteArrayInputStream
(
data
,
0
,
limit
)
);
Assertions
.
checkState
(
playlist
.
type
==
HlsPlaylist
.
TYPE_MEDIA
);
Assertions
.
checkState
(
playlist
.
type
==
HlsPlaylist
.
TYPE_MEDIA
);
HlsMediaPlaylist
mediaPlaylist
=
(
HlsMediaPlaylist
)
playlist
;
HlsMediaPlaylist
mediaPlaylist
=
(
HlsMediaPlaylist
)
playlist
;
setMediaPlaylist
(
variantIndex
,
mediaPlaylist
);
setMediaPlaylist
(
variantIndex
,
mediaPlaylist
);
...
...
library/src/main/java/com/google/android/exoplayer/hls/HlsPlaylistParser.java
View file @
7f70db97
...
@@ -18,7 +18,8 @@ package com.google.android.exoplayer.hls;
...
@@ -18,7 +18,8 @@ package com.google.android.exoplayer.hls;
import
com.google.android.exoplayer.C
;
import
com.google.android.exoplayer.C
;
import
com.google.android.exoplayer.ParserException
;
import
com.google.android.exoplayer.ParserException
;
import
com.google.android.exoplayer.hls.HlsMediaPlaylist.Segment
;
import
com.google.android.exoplayer.hls.HlsMediaPlaylist.Segment
;
import
com.google.android.exoplayer.util.ManifestParser
;
import
com.google.android.exoplayer.upstream.NetworkLoadable
;
import
com.google.android.exoplayer.util.Util
;
import
android.net.Uri
;
import
android.net.Uri
;
...
@@ -36,7 +37,7 @@ import java.util.regex.Pattern;
...
@@ -36,7 +37,7 @@ import java.util.regex.Pattern;
/**
/**
* HLS playlists parsing logic.
* HLS playlists parsing logic.
*/
*/
public
final
class
HlsPlaylistParser
implements
Manifest
Parser
<
HlsPlaylist
>
{
public
final
class
HlsPlaylistParser
implements
NetworkLoadable
.
Parser
<
HlsPlaylist
>
{
private
static
final
String
VERSION_TAG
=
"#EXT-X-VERSION"
;
private
static
final
String
VERSION_TAG
=
"#EXT-X-VERSION"
;
...
@@ -83,10 +84,10 @@ public final class HlsPlaylistParser implements ManifestParser<HlsPlaylist> {
...
@@ -83,10 +84,10 @@ public final class HlsPlaylistParser implements ManifestParser<HlsPlaylist> {
Pattern
.
compile
(
IV_ATTR
+
"=([^,.*]+)"
);
Pattern
.
compile
(
IV_ATTR
+
"=([^,.*]+)"
);
@Override
@Override
public
HlsPlaylist
parse
(
InputStream
inputStream
,
String
inputEncoding
,
public
HlsPlaylist
parse
(
String
connectionUrl
,
InputStream
inputStream
)
String
contentId
,
Uri
baseUri
)
throws
IO
Exception
{
throws
IOException
,
Parser
Exception
{
BufferedReader
reader
=
new
BufferedReader
((
inputEncoding
==
null
)
Uri
baseUri
=
Util
.
parseBaseUri
(
connectionUrl
);
?
new
InputStreamReader
(
inputStream
)
:
new
InputStreamReader
(
inputStream
,
inputEncoding
));
BufferedReader
reader
=
new
BufferedReader
(
new
InputStreamReader
(
inputStream
));
Queue
<
String
>
extraLines
=
new
LinkedList
<
String
>();
Queue
<
String
>
extraLines
=
new
LinkedList
<
String
>();
String
line
;
String
line
;
try
{
try
{
...
...
library/src/main/java/com/google/android/exoplayer/hls/HlsSampleSource.java
View file @
7f70db97
...
@@ -164,7 +164,7 @@ public class HlsSampleSource implements SampleSource, Loader.Callback {
...
@@ -164,7 +164,7 @@ public class HlsSampleSource implements SampleSource, Loader.Callback {
if
(!
extractors
.
isEmpty
())
{
if
(!
extractors
.
isEmpty
())
{
discardSamplesForDisabledTracks
(
extractors
.
getFirst
(),
downstreamPositionUs
);
discardSamplesForDisabledTracks
(
extractors
.
getFirst
(),
downstreamPositionUs
);
}
}
return
continueBufferingInternal
();
return
loadingFinished
||
continueBufferingInternal
();
}
}
private
boolean
continueBufferingInternal
()
throws
IOException
{
private
boolean
continueBufferingInternal
()
throws
IOException
{
...
...
library/src/main/java/com/google/android/exoplayer/mp4/Mp4Util.java
View file @
7f70db97
...
@@ -171,6 +171,9 @@ public final class Mp4Util {
...
@@ -171,6 +171,9 @@ public final class Mp4Util {
// loop advance the index by three.
// loop advance the index by three.
}
else
if
(
data
[
i
-
2
]
==
0
&&
data
[
i
-
1
]
==
0
&&
data
[
i
]
==
1
}
else
if
(
data
[
i
-
2
]
==
0
&&
data
[
i
-
1
]
==
0
&&
data
[
i
]
==
1
&&
matchesType
(
data
,
i
+
1
,
type
))
{
&&
matchesType
(
data
,
i
+
1
,
type
))
{
if
(
prefixFlags
!=
null
)
{
clearPrefixFlags
(
prefixFlags
);
}
return
i
-
2
;
return
i
-
2
;
}
else
{
}
else
{
// There isn't a NAL prefix here, but there might be at the next position. We should
// There isn't a NAL prefix here, but there might be at the next position. We should
...
...
library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingManifestParser.java
View file @
7f70db97
...
@@ -19,10 +19,11 @@ import com.google.android.exoplayer.ParserException;
...
@@ -19,10 +19,11 @@ import com.google.android.exoplayer.ParserException;
import
com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest.ProtectionElement
;
import
com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest.ProtectionElement
;
import
com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest.StreamElement
;
import
com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest.StreamElement
;
import
com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest.TrackElement
;
import
com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest.TrackElement
;
import
com.google.android.exoplayer.upstream.NetworkLoadable
;
import
com.google.android.exoplayer.util.Assertions
;
import
com.google.android.exoplayer.util.Assertions
;
import
com.google.android.exoplayer.util.CodecSpecificDataUtil
;
import
com.google.android.exoplayer.util.CodecSpecificDataUtil
;
import
com.google.android.exoplayer.util.ManifestParser
;
import
com.google.android.exoplayer.util.MimeTypes
;
import
com.google.android.exoplayer.util.MimeTypes
;
import
com.google.android.exoplayer.util.Util
;
import
android.net.Uri
;
import
android.net.Uri
;
import
android.util.Base64
;
import
android.util.Base64
;
...
@@ -45,7 +46,8 @@ import java.util.UUID;
...
@@ -45,7 +46,8 @@ import java.util.UUID;
* @see <a href="http://msdn.microsoft.com/en-us/library/ee673436(v=vs.90).aspx">
* @see <a href="http://msdn.microsoft.com/en-us/library/ee673436(v=vs.90).aspx">
* IIS Smooth Streaming Client Manifest Format</a>
* IIS Smooth Streaming Client Manifest Format</a>
*/
*/
public
class
SmoothStreamingManifestParser
implements
ManifestParser
<
SmoothStreamingManifest
>
{
public
class
SmoothStreamingManifestParser
implements
NetworkLoadable
.
Parser
<
SmoothStreamingManifest
>
{
private
final
XmlPullParserFactory
xmlParserFactory
;
private
final
XmlPullParserFactory
xmlParserFactory
;
...
@@ -58,12 +60,13 @@ public class SmoothStreamingManifestParser implements ManifestParser<SmoothStrea
...
@@ -58,12 +60,13 @@ public class SmoothStreamingManifestParser implements ManifestParser<SmoothStrea
}
}
@Override
@Override
public
SmoothStreamingManifest
parse
(
InputStream
inputStream
,
String
inputEncoding
,
public
SmoothStreamingManifest
parse
(
String
connectionUrl
,
InputStream
inputStream
)
String
contentId
,
Uri
baseUri
)
throws
IOException
,
ParserException
{
throws
IOException
,
ParserException
{
try
{
try
{
XmlPullParser
xmlParser
=
xmlParserFactory
.
newPullParser
();
XmlPullParser
xmlParser
=
xmlParserFactory
.
newPullParser
();
xmlParser
.
setInput
(
inputStream
,
inputEncoding
);
xmlParser
.
setInput
(
inputStream
,
null
);
SmoothStreamMediaParser
smoothStreamMediaParser
=
new
SmoothStreamMediaParser
(
null
,
baseUri
);
SmoothStreamMediaParser
smoothStreamMediaParser
=
new
SmoothStreamMediaParser
(
null
,
Util
.
parseBaseUri
(
connectionUrl
));
return
(
SmoothStreamingManifest
)
smoothStreamMediaParser
.
parse
(
xmlParser
);
return
(
SmoothStreamingManifest
)
smoothStreamMediaParser
.
parse
(
xmlParser
);
}
catch
(
XmlPullParserException
e
)
{
}
catch
(
XmlPullParserException
e
)
{
throw
new
ParserException
(
e
);
throw
new
ParserException
(
e
);
...
...
library/src/main/java/com/google/android/exoplayer/upstream/DataSourceInputStream.java
View file @
7f70db97
...
@@ -43,6 +43,19 @@ public class DataSourceInputStream extends InputStream {
...
@@ -43,6 +43,19 @@ public class DataSourceInputStream extends InputStream {
singleByteArray
=
new
byte
[
1
];
singleByteArray
=
new
byte
[
1
];
}
}
/**
* Optional call to open the underlying {@link DataSource}.
* <p>
* Calling this method does nothing if the {@link DataSource} is already open. Calling this
* method is optional, since the read and skip methods will automatically open the underlying
* {@link DataSource} if it's not open already.
*
* @throws IOException If an error occurs opening the {@link DataSource}.
*/
public
void
open
()
throws
IOException
{
checkOpened
();
}
@Override
@Override
public
int
read
()
throws
IOException
{
public
int
read
()
throws
IOException
{
read
(
singleByteArray
);
read
(
singleByteArray
);
...
...
library/src/main/java/com/google/android/exoplayer/upstream/DefaultHttpDataSource.java
0 → 100644
View file @
7f70db97
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
com
.
google
.
android
.
exoplayer
.
upstream
;
import
com.google.android.exoplayer.C
;
import
com.google.android.exoplayer.util.Assertions
;
import
com.google.android.exoplayer.util.Predicate
;
import
android.text.TextUtils
;
import
android.util.Log
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.net.HttpURLConnection
;
import
java.net.URL
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.regex.Matcher
;
import
java.util.regex.Pattern
;
/**
* A {@link HttpDataSource} that uses Android's {@link HttpURLConnection}.
*/
public
class
DefaultHttpDataSource
implements
HttpDataSource
{
public
static
final
int
DEFAULT_CONNECT_TIMEOUT_MILLIS
=
8
*
1000
;
public
static
final
int
DEFAULT_READ_TIMEOUT_MILLIS
=
8
*
1000
;
private
static
final
String
TAG
=
"HttpDataSource"
;
private
static
final
Pattern
CONTENT_RANGE_HEADER
=
Pattern
.
compile
(
"^bytes (\\d+)-(\\d+)/(\\d+)$"
);
private
final
int
connectTimeoutMillis
;
private
final
int
readTimeoutMillis
;
private
final
String
userAgent
;
private
final
Predicate
<
String
>
contentTypePredicate
;
private
final
HashMap
<
String
,
String
>
requestProperties
;
private
final
TransferListener
listener
;
private
DataSpec
dataSpec
;
private
HttpURLConnection
connection
;
private
InputStream
inputStream
;
private
boolean
opened
;
private
long
dataLength
;
private
long
bytesRead
;
/**
* @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)}.
*/
public
DefaultHttpDataSource
(
String
userAgent
,
Predicate
<
String
>
contentTypePredicate
)
{
this
(
userAgent
,
contentTypePredicate
,
null
);
}
/**
* @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.
*/
public
DefaultHttpDataSource
(
String
userAgent
,
Predicate
<
String
>
contentTypePredicate
,
TransferListener
listener
)
{
this
(
userAgent
,
contentTypePredicate
,
listener
,
DEFAULT_CONNECT_TIMEOUT_MILLIS
,
DEFAULT_READ_TIMEOUT_MILLIS
);
}
/**
* @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.
* @param readTimeoutMillis The read timeout, in milliseconds. A timeout of zero is interpreted
* as an infinite timeout.
*/
public
DefaultHttpDataSource
(
String
userAgent
,
Predicate
<
String
>
contentTypePredicate
,
TransferListener
listener
,
int
connectTimeoutMillis
,
int
readTimeoutMillis
)
{
this
.
userAgent
=
Assertions
.
checkNotEmpty
(
userAgent
);
this
.
contentTypePredicate
=
contentTypePredicate
;
this
.
listener
=
listener
;
this
.
requestProperties
=
new
HashMap
<
String
,
String
>();
this
.
connectTimeoutMillis
=
connectTimeoutMillis
;
this
.
readTimeoutMillis
=
readTimeoutMillis
;
}
@Override
public
String
getUrl
()
{
return
connection
==
null
?
null
:
connection
.
getURL
().
toString
();
}
@Override
public
void
setRequestProperty
(
String
name
,
String
value
)
{
Assertions
.
checkNotNull
(
name
);
Assertions
.
checkNotNull
(
value
);
synchronized
(
requestProperties
)
{
requestProperties
.
put
(
name
,
value
);
}
}
@Override
public
void
clearRequestProperty
(
String
name
)
{
Assertions
.
checkNotNull
(
name
);
synchronized
(
requestProperties
)
{
requestProperties
.
remove
(
name
);
}
}
@Override
public
void
clearAllRequestProperties
()
{
synchronized
(
requestProperties
)
{
requestProperties
.
clear
();
}
}
/*
* TODO: If the server uses gzip compression when serving the response, this may end up returning
* the size of the compressed response, where-as it should be returning the decompressed size or
* -1. See: developer.android.com/reference/java/net/HttpURLConnection.html
*
* To fix this we should:
*
* 1. Explicitly require no compression for media requests (since media should be compressed
* already) by setting the Accept-Encoding header to "identity"
* 2. In other cases, for example when requesting manifests, we don't want to disable compression.
* For these cases we should ensure that we return -1 here (and avoid performing any sanity
* checks on the content length).
*/
@Override
public
long
open
(
DataSpec
dataSpec
)
throws
HttpDataSourceException
{
this
.
dataSpec
=
dataSpec
;
this
.
bytesRead
=
0
;
try
{
connection
=
makeConnection
(
dataSpec
);
}
catch
(
IOException
e
)
{
throw
new
HttpDataSourceException
(
"Unable to connect to "
+
dataSpec
.
uri
.
toString
(),
e
,
dataSpec
);
}
// Check for a valid response code.
int
responseCode
;
try
{
responseCode
=
connection
.
getResponseCode
();
}
catch
(
IOException
e
)
{
throw
new
HttpDataSourceException
(
"Unable to connect to "
+
dataSpec
.
uri
.
toString
(),
e
,
dataSpec
);
}
if
(
responseCode
<
200
||
responseCode
>
299
)
{
Map
<
String
,
List
<
String
>>
headers
=
connection
.
getHeaderFields
();
closeConnection
();
throw
new
InvalidResponseCodeException
(
responseCode
,
headers
,
dataSpec
);
}
// Check for a valid content type.
String
contentType
=
connection
.
getContentType
();
if
(
contentTypePredicate
!=
null
&&
!
contentTypePredicate
.
evaluate
(
contentType
))
{
closeConnection
();
throw
new
InvalidContentTypeException
(
contentType
,
dataSpec
);
}
long
contentLength
=
getContentLength
(
connection
);
dataLength
=
dataSpec
.
length
==
C
.
LENGTH_UNBOUNDED
?
contentLength
:
dataSpec
.
length
;
if
(
dataSpec
.
length
!=
C
.
LENGTH_UNBOUNDED
&&
contentLength
!=
C
.
LENGTH_UNBOUNDED
&&
contentLength
!=
dataSpec
.
length
)
{
// The DataSpec specified a length and we resolved a length from the response headers, but
// the two lengths do not match.
closeConnection
();
throw
new
HttpDataSourceException
(
new
UnexpectedLengthException
(
dataSpec
.
length
,
contentLength
),
dataSpec
);
}
try
{
inputStream
=
connection
.
getInputStream
();
}
catch
(
IOException
e
)
{
closeConnection
();
throw
new
HttpDataSourceException
(
e
,
dataSpec
);
}
opened
=
true
;
if
(
listener
!=
null
)
{
listener
.
onTransferStart
();
}
return
dataLength
;
}
@Override
public
int
read
(
byte
[]
buffer
,
int
offset
,
int
readLength
)
throws
HttpDataSourceException
{
int
read
=
0
;
try
{
read
=
inputStream
.
read
(
buffer
,
offset
,
readLength
);
}
catch
(
IOException
e
)
{
throw
new
HttpDataSourceException
(
e
,
dataSpec
);
}
if
(
read
>
0
)
{
bytesRead
+=
read
;
if
(
listener
!=
null
)
{
listener
.
onBytesTransferred
(
read
);
}
}
else
if
(
dataLength
!=
C
.
LENGTH_UNBOUNDED
&&
dataLength
!=
bytesRead
)
{
// Check for cases where the server closed the connection having not sent the correct amount
// of data. We can only do this if we know the length of the data we were expecting.
throw
new
HttpDataSourceException
(
new
UnexpectedLengthException
(
dataLength
,
bytesRead
),
dataSpec
);
}
return
read
;
}
@Override
public
void
close
()
throws
HttpDataSourceException
{
try
{
if
(
inputStream
!=
null
)
{
try
{
inputStream
.
close
();
}
catch
(
IOException
e
)
{
throw
new
HttpDataSourceException
(
e
,
dataSpec
);
}
inputStream
=
null
;
}
}
finally
{
if
(
opened
)
{
opened
=
false
;
if
(
listener
!=
null
)
{
listener
.
onTransferEnd
();
}
closeConnection
();
}
}
}
private
void
closeConnection
()
{
if
(
connection
!=
null
)
{
connection
.
disconnect
();
connection
=
null
;
}
}
/**
* Returns the current connection, or null if the source is not currently opened.
*
* @return The current open connection, or null.
*/
protected
final
HttpURLConnection
getConnection
()
{
return
connection
;
}
/**
* Returns the number of bytes that have been read since the most recent call to
* {@link #open(DataSpec)}.
*
* @return The number of bytes read.
*/
protected
final
long
bytesRead
()
{
return
bytesRead
;
}
/**
* Returns the number of bytes that are still to be read for the current {@link DataSpec}.
* <p>
* If the total length of the data being read is known, then this length minus {@code bytesRead()}
* is returned. If the total length is unknown, {@link C#LENGTH_UNBOUNDED} is returned.
*
* @return The remaining length, or {@link C#LENGTH_UNBOUNDED}.
*/
protected
final
long
bytesRemaining
()
{
return
dataLength
==
C
.
LENGTH_UNBOUNDED
?
dataLength
:
dataLength
-
bytesRead
;
}
private
HttpURLConnection
makeConnection
(
DataSpec
dataSpec
)
throws
IOException
{
URL
url
=
new
URL
(
dataSpec
.
uri
.
toString
());
HttpURLConnection
connection
=
(
HttpURLConnection
)
url
.
openConnection
();
connection
.
setConnectTimeout
(
connectTimeoutMillis
);
connection
.
setReadTimeout
(
readTimeoutMillis
);
connection
.
setDoOutput
(
false
);
synchronized
(
requestProperties
)
{
for
(
Map
.
Entry
<
String
,
String
>
property
:
requestProperties
.
entrySet
())
{
connection
.
setRequestProperty
(
property
.
getKey
(),
property
.
getValue
());
}
}
setRangeHeader
(
connection
,
dataSpec
);
connection
.
setRequestProperty
(
"User-Agent"
,
userAgent
);
connection
.
connect
();
return
connection
;
}
private
void
setRangeHeader
(
HttpURLConnection
connection
,
DataSpec
dataSpec
)
{
if
(
dataSpec
.
position
==
0
&&
dataSpec
.
length
==
C
.
LENGTH_UNBOUNDED
)
{
// Not required.
return
;
}
String
rangeRequest
=
"bytes="
+
dataSpec
.
position
+
"-"
;
if
(
dataSpec
.
length
!=
C
.
LENGTH_UNBOUNDED
)
{
rangeRequest
+=
(
dataSpec
.
position
+
dataSpec
.
length
-
1
);
}
connection
.
setRequestProperty
(
"Range"
,
rangeRequest
);
}
private
long
getContentLength
(
HttpURLConnection
connection
)
{
long
contentLength
=
C
.
LENGTH_UNBOUNDED
;
String
contentLengthHeader
=
connection
.
getHeaderField
(
"Content-Length"
);
if
(!
TextUtils
.
isEmpty
(
contentLengthHeader
))
{
try
{
contentLength
=
Long
.
parseLong
(
contentLengthHeader
);
}
catch
(
NumberFormatException
e
)
{
Log
.
e
(
TAG
,
"Unexpected Content-Length ["
+
contentLengthHeader
+
"]"
);
}
}
String
contentRangeHeader
=
connection
.
getHeaderField
(
"Content-Range"
);
if
(!
TextUtils
.
isEmpty
(
contentRangeHeader
))
{
Matcher
matcher
=
CONTENT_RANGE_HEADER
.
matcher
(
contentRangeHeader
);
if
(
matcher
.
find
())
{
try
{
long
contentLengthFromRange
=
Long
.
parseLong
(
matcher
.
group
(
2
))
-
Long
.
parseLong
(
matcher
.
group
(
1
))
+
1
;
if
(
contentLength
<
0
)
{
// Some proxy servers strip the Content-Length header. Fall back to the length
// calculated here in this case.
contentLength
=
contentLengthFromRange
;
}
else
if
(
contentLength
!=
contentLengthFromRange
)
{
// If there is a discrepancy between the Content-Length and Content-Range headers,
// assume the one with the larger value is correct. We have seen cases where carrier
// change one of them to reduce the size of a request, but it is unlikely anybody would
// increase it.
Log
.
w
(
TAG
,
"Inconsistent headers ["
+
contentLengthHeader
+
"] ["
+
contentRangeHeader
+
"]"
);
contentLength
=
Math
.
max
(
contentLength
,
contentLengthFromRange
);
}
}
catch
(
NumberFormatException
e
)
{
Log
.
e
(
TAG
,
"Unexpected Content-Range ["
+
contentRangeHeader
+
"]"
);
}
}
}
return
contentLength
;
}
}
library/src/main/java/com/google/android/exoplayer/upstream/HttpDataSource.java
View file @
7f70db97
...
@@ -15,28 +15,20 @@
...
@@ -15,28 +15,20 @@
*/
*/
package
com
.
google
.
android
.
exoplayer
.
upstream
;
package
com
.
google
.
android
.
exoplayer
.
upstream
;
import
com.google.android.exoplayer.C
;
import
com.google.android.exoplayer.util.Assertions
;
import
com.google.android.exoplayer.util.Predicate
;
import
com.google.android.exoplayer.util.Predicate
;
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.Log
;
import
java.io.IOException
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.net.HttpURLConnection
;
import
java.net.URL
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Map
;
import
java.util.regex.Matcher
;
import
java.util.regex.Pattern
;
/**
/**
* An
http
{@link DataSource}.
* An
HTTP specific extension to
{@link DataSource}.
*/
*/
public
class
HttpDataSource
implement
s
DataSource
{
public
interface
HttpDataSource
extend
s
DataSource
{
/**
/**
* A {@link Predicate} that rejects content types often used for pay-walls.
* A {@link Predicate} that rejects content types often used for pay-walls.
...
@@ -54,7 +46,7 @@ public class HttpDataSource implements DataSource {
...
@@ -54,7 +46,7 @@ public class HttpDataSource implements DataSource {
};
};
/**
/**
* Thrown when an error is encountered when trying to read from
HTTP data source
.
* Thrown when an error is encountered when trying to read from
a {@link HttpDataSource}
.
*/
*/
public
static
class
HttpDataSourceException
extends
IOException
{
public
static
class
HttpDataSourceException
extends
IOException
{
...
@@ -123,71 +115,14 @@ public class HttpDataSource implements DataSource {
...
@@ -123,71 +115,14 @@ public class HttpDataSource implements DataSource {
}
}
public
static
final
int
DEFAULT_CONNECT_TIMEOUT_MILLIS
=
8
*
1000
;
public
static
final
int
DEFAULT_READ_TIMEOUT_MILLIS
=
8
*
1000
;
private
static
final
String
TAG
=
"HttpDataSource"
;
private
static
final
Pattern
CONTENT_RANGE_HEADER
=
Pattern
.
compile
(
"^bytes (\\d+)-(\\d+)/(\\d+)$"
);
private
final
int
connectTimeoutMillis
;
private
final
int
readTimeoutMillis
;
private
final
String
userAgent
;
private
final
Predicate
<
String
>
contentTypePredicate
;
private
final
HashMap
<
String
,
String
>
requestProperties
;
private
final
TransferListener
listener
;
private
DataSpec
dataSpec
;
private
HttpURLConnection
connection
;
private
InputStream
inputStream
;
private
boolean
opened
;
private
long
dataLength
;
private
long
bytesRead
;
/**
/**
* @param userAgent The User-Agent string that should be used.
* When the source is open, returns the url from which data is being read.
* @param contentTypePredicate An optional {@link Predicate}. If a content type is
* <p>
* rejected by the predicate then a {@link InvalidContentTypeException} is thrown from
* If redirection occurred, the url after redirection is the one returned.
* {@link #open(DataSpec)}.
*
*/
* @return When the source is open, the url from which data is being read. Null otherwise.
public
HttpDataSource
(
String
userAgent
,
Predicate
<
String
>
contentTypePredicate
)
{
this
(
userAgent
,
contentTypePredicate
,
null
);
}
/**
* @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 InvalidContentTypeException} is thrown from
* {@link #open(DataSpec)}.
* @param listener An optional listener.
*/
public
HttpDataSource
(
String
userAgent
,
Predicate
<
String
>
contentTypePredicate
,
TransferListener
listener
)
{
this
(
userAgent
,
contentTypePredicate
,
listener
,
DEFAULT_CONNECT_TIMEOUT_MILLIS
,
DEFAULT_READ_TIMEOUT_MILLIS
);
}
/**
* @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 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.
* @param readTimeoutMillis The read timeout, in milliseconds. A timeout of zero is interpreted
* as an infinite timeout.
*/
*/
public
HttpDataSource
(
String
userAgent
,
Predicate
<
String
>
contentTypePredicate
,
String
getUrl
();
TransferListener
listener
,
int
connectTimeoutMillis
,
int
readTimeoutMillis
)
{
this
.
userAgent
=
Assertions
.
checkNotEmpty
(
userAgent
);
this
.
contentTypePredicate
=
contentTypePredicate
;
this
.
listener
=
listener
;
this
.
requestProperties
=
new
HashMap
<
String
,
String
>();
this
.
connectTimeoutMillis
=
connectTimeoutMillis
;
this
.
readTimeoutMillis
=
readTimeoutMillis
;
}
/**
/**
* Sets the value of a request header field. The value will be used for subsequent connections
* Sets the value of a request header field. The value will be used for subsequent connections
...
@@ -196,13 +131,7 @@ public class HttpDataSource implements DataSource {
...
@@ -196,13 +131,7 @@ public class HttpDataSource implements DataSource {
* @param name The name of the header field.
* @param name The name of the header field.
* @param value The value of the field.
* @param value The value of the field.
*/
*/
public
void
setRequestProperty
(
String
name
,
String
value
)
{
void
setRequestProperty
(
String
name
,
String
value
);
Assertions
.
checkNotNull
(
name
);
Assertions
.
checkNotNull
(
value
);
synchronized
(
requestProperties
)
{
requestProperties
.
put
(
name
,
value
);
}
}
/**
/**
* Clears the value of a request header field. The change will apply to subsequent connections
* Clears the value of a request header field. The change will apply to subsequent connections
...
@@ -210,243 +139,11 @@ public class HttpDataSource implements DataSource {
...
@@ -210,243 +139,11 @@ public class HttpDataSource implements DataSource {
*
*
* @param name The name of the header field.
* @param name The name of the header field.
*/
*/
public
void
clearRequestProperty
(
String
name
)
{
void
clearRequestProperty
(
String
name
);
Assertions
.
checkNotNull
(
name
);
synchronized
(
requestProperties
)
{
requestProperties
.
remove
(
name
);
}
}
/**
/**
* Clears all request header fields that were set by {@link #setRequestProperty(String, String)}.
* Clears all request header fields that were set by {@link #setRequestProperty(String, String)}.
*/
*/
public
void
clearAllRequestProperties
()
{
void
clearAllRequestProperties
();
synchronized
(
requestProperties
)
{
requestProperties
.
clear
();
}
}
/*
* TODO: If the server uses gzip compression when serving the response, this may end up returning
* the size of the compressed response, where-as it should be returning the decompressed size or
* -1. See: developer.android.com/reference/java/net/HttpURLConnection.html
*
* To fix this we should:
*
* 1. Explicitly require no compression for media requests (since media should be compressed
* already) by setting the Accept-Encoding header to "identity"
* 2. In other cases, for example when requesting manifests, we don't want to disable compression.
* For these cases we should ensure that we return -1 here (and avoid performing any sanity
* checks on the content length).
*/
@Override
public
long
open
(
DataSpec
dataSpec
)
throws
HttpDataSourceException
{
this
.
dataSpec
=
dataSpec
;
this
.
bytesRead
=
0
;
try
{
connection
=
makeConnection
(
dataSpec
);
}
catch
(
IOException
e
)
{
throw
new
HttpDataSourceException
(
"Unable to connect to "
+
dataSpec
.
uri
.
toString
(),
e
,
dataSpec
);
}
// Check for a valid response code.
int
responseCode
;
try
{
responseCode
=
connection
.
getResponseCode
();
}
catch
(
IOException
e
)
{
throw
new
HttpDataSourceException
(
"Unable to connect to "
+
dataSpec
.
uri
.
toString
(),
e
,
dataSpec
);
}
if
(
responseCode
<
200
||
responseCode
>
299
)
{
Map
<
String
,
List
<
String
>>
headers
=
connection
.
getHeaderFields
();
closeConnection
();
throw
new
InvalidResponseCodeException
(
responseCode
,
headers
,
dataSpec
);
}
// Check for a valid content type.
String
contentType
=
connection
.
getContentType
();
if
(
contentTypePredicate
!=
null
&&
!
contentTypePredicate
.
evaluate
(
contentType
))
{
closeConnection
();
throw
new
InvalidContentTypeException
(
contentType
,
dataSpec
);
}
long
contentLength
=
getContentLength
(
connection
);
dataLength
=
dataSpec
.
length
==
C
.
LENGTH_UNBOUNDED
?
contentLength
:
dataSpec
.
length
;
if
(
dataSpec
.
length
!=
C
.
LENGTH_UNBOUNDED
&&
contentLength
!=
C
.
LENGTH_UNBOUNDED
&&
contentLength
!=
dataSpec
.
length
)
{
// The DataSpec specified a length and we resolved a length from the response headers, but
// the two lengths do not match.
closeConnection
();
throw
new
HttpDataSourceException
(
new
UnexpectedLengthException
(
dataSpec
.
length
,
contentLength
),
dataSpec
);
}
try
{
inputStream
=
connection
.
getInputStream
();
}
catch
(
IOException
e
)
{
closeConnection
();
throw
new
HttpDataSourceException
(
e
,
dataSpec
);
}
opened
=
true
;
if
(
listener
!=
null
)
{
listener
.
onTransferStart
();
}
return
dataLength
;
}
@Override
public
int
read
(
byte
[]
buffer
,
int
offset
,
int
readLength
)
throws
HttpDataSourceException
{
int
read
=
0
;
try
{
read
=
inputStream
.
read
(
buffer
,
offset
,
readLength
);
}
catch
(
IOException
e
)
{
throw
new
HttpDataSourceException
(
e
,
dataSpec
);
}
if
(
read
>
0
)
{
bytesRead
+=
read
;
if
(
listener
!=
null
)
{
listener
.
onBytesTransferred
(
read
);
}
}
else
if
(
dataLength
!=
C
.
LENGTH_UNBOUNDED
&&
dataLength
!=
bytesRead
)
{
// Check for cases where the server closed the connection having not sent the correct amount
// of data. We can only do this if we know the length of the data we were expecting.
throw
new
HttpDataSourceException
(
new
UnexpectedLengthException
(
dataLength
,
bytesRead
),
dataSpec
);
}
return
read
;
}
@Override
public
void
close
()
throws
HttpDataSourceException
{
try
{
if
(
inputStream
!=
null
)
{
try
{
inputStream
.
close
();
}
catch
(
IOException
e
)
{
throw
new
HttpDataSourceException
(
e
,
dataSpec
);
}
inputStream
=
null
;
}
}
finally
{
if
(
opened
)
{
opened
=
false
;
if
(
listener
!=
null
)
{
listener
.
onTransferEnd
();
}
closeConnection
();
}
}
}
private
void
closeConnection
()
{
if
(
connection
!=
null
)
{
connection
.
disconnect
();
connection
=
null
;
}
}
/**
* Returns the current connection, or null if the source is not currently opened.
*
* @return The current open connection, or null.
*/
protected
final
HttpURLConnection
getConnection
()
{
return
connection
;
}
/**
* Returns the number of bytes that have been read since the most recent call to
* {@link #open(DataSpec)}.
*
* @return The number of bytes read.
*/
protected
final
long
bytesRead
()
{
return
bytesRead
;
}
/**
* Returns the number of bytes that are still to be read for the current {@link DataSpec}.
* <p>
* If the total length of the data being read is known, then this length minus {@code bytesRead()}
* is returned. If the total length is unknown, {@link C#LENGTH_UNBOUNDED} is returned.
*
* @return The remaining length, or {@link C#LENGTH_UNBOUNDED}.
*/
protected
final
long
bytesRemaining
()
{
return
dataLength
==
C
.
LENGTH_UNBOUNDED
?
dataLength
:
dataLength
-
bytesRead
;
}
private
HttpURLConnection
makeConnection
(
DataSpec
dataSpec
)
throws
IOException
{
URL
url
=
new
URL
(
dataSpec
.
uri
.
toString
());
HttpURLConnection
connection
=
(
HttpURLConnection
)
url
.
openConnection
();
connection
.
setConnectTimeout
(
connectTimeoutMillis
);
connection
.
setReadTimeout
(
readTimeoutMillis
);
connection
.
setDoOutput
(
false
);
synchronized
(
requestProperties
)
{
for
(
Map
.
Entry
<
String
,
String
>
property
:
requestProperties
.
entrySet
())
{
connection
.
setRequestProperty
(
property
.
getKey
(),
property
.
getValue
());
}
}
setRangeHeader
(
connection
,
dataSpec
);
connection
.
setRequestProperty
(
"User-Agent"
,
userAgent
);
connection
.
connect
();
return
connection
;
}
private
void
setRangeHeader
(
HttpURLConnection
connection
,
DataSpec
dataSpec
)
{
if
(
dataSpec
.
position
==
0
&&
dataSpec
.
length
==
C
.
LENGTH_UNBOUNDED
)
{
// Not required.
return
;
}
String
rangeRequest
=
"bytes="
+
dataSpec
.
position
+
"-"
;
if
(
dataSpec
.
length
!=
C
.
LENGTH_UNBOUNDED
)
{
rangeRequest
+=
(
dataSpec
.
position
+
dataSpec
.
length
-
1
);
}
connection
.
setRequestProperty
(
"Range"
,
rangeRequest
);
}
private
long
getContentLength
(
HttpURLConnection
connection
)
{
long
contentLength
=
C
.
LENGTH_UNBOUNDED
;
String
contentLengthHeader
=
connection
.
getHeaderField
(
"Content-Length"
);
if
(!
TextUtils
.
isEmpty
(
contentLengthHeader
))
{
try
{
contentLength
=
Long
.
parseLong
(
contentLengthHeader
);
}
catch
(
NumberFormatException
e
)
{
Log
.
e
(
TAG
,
"Unexpected Content-Length ["
+
contentLengthHeader
+
"]"
);
}
}
String
contentRangeHeader
=
connection
.
getHeaderField
(
"Content-Range"
);
if
(!
TextUtils
.
isEmpty
(
contentRangeHeader
))
{
Matcher
matcher
=
CONTENT_RANGE_HEADER
.
matcher
(
contentRangeHeader
);
if
(
matcher
.
find
())
{
try
{
long
contentLengthFromRange
=
Long
.
parseLong
(
matcher
.
group
(
2
))
-
Long
.
parseLong
(
matcher
.
group
(
1
))
+
1
;
if
(
contentLength
<
0
)
{
// Some proxy servers strip the Content-Length header. Fall back to the length
// calculated here in this case.
contentLength
=
contentLengthFromRange
;
}
else
if
(
contentLength
!=
contentLengthFromRange
)
{
// If there is a discrepancy between the Content-Length and Content-Range headers,
// assume the one with the larger value is correct. We have seen cases where carrier
// change one of them to reduce the size of a request, but it is unlikely anybody would
// increase it.
Log
.
w
(
TAG
,
"Inconsistent headers ["
+
contentLengthHeader
+
"] ["
+
contentRangeHeader
+
"]"
);
contentLength
=
Math
.
max
(
contentLength
,
contentLengthFromRange
);
}
}
catch
(
NumberFormatException
e
)
{
Log
.
e
(
TAG
,
"Unexpected Content-Range ["
+
contentRangeHeader
+
"]"
);
}
}
}
return
contentLength
;
}
}
}
library/src/main/java/com/google/android/exoplayer/u
til
/NetworkLoadable.java
→
library/src/main/java/com/google/android/exoplayer/u
pstream
/NetworkLoadable.java
View file @
7f70db97
...
@@ -13,49 +13,57 @@
...
@@ -13,49 +13,57 @@
* See the License for the specific language governing permissions and
* See the License for the specific language governing permissions and
* limitations under the License.
* limitations under the License.
*/
*/
package
com
.
google
.
android
.
exoplayer
.
u
til
;
package
com
.
google
.
android
.
exoplayer
.
u
pstream
;
import
com.google.android.exoplayer.ParserException
;
import
com.google.android.exoplayer.ParserException
;
import
com.google.android.exoplayer.upstream.Loader.Loadable
;
import
com.google.android.exoplayer.upstream.Loader.Loadable
;
import
android.net.Uri
;
import
java.io.IOException
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.io.InputStream
;
import
java.net.URL
;
import
java.net.URLConnection
;
/**
/**
* A {@link Loadable} for loading an object over the network.
* A {@link Loadable} for loading an object over the network.
*
*
* @param <T> The type of the object being loaded.
* @param <T> The type of the object being loaded.
*/
*/
public
abstract
class
NetworkLoadable
<
T
>
implements
Loadable
{
public
final
class
NetworkLoadable
<
T
>
implements
Loadable
{
/**
* Parses an object from data loaded over the network.
*/
public
interface
Parser
<
T
>
{
public
static
final
int
DEFAULT_TIMEOUT_MILLIS
=
10000
;
/**
* Parses an object from a network response.
*
* @param connectionUrl The source of the response, after any redirection.
* @param inputStream An {@link InputStream} from which the response data can be read.
* @return The parsed object.
* @throws ParserException If an error occurs parsing the data.
* @throws IOException If an error occurs reading data from the stream.
*/
T
parse
(
String
connectionUrl
,
InputStream
inputStream
)
throws
ParserException
,
IOException
;
private
final
String
url
;
}
private
final
String
userAgent
;
private
final
int
timeoutMillis
;
private
final
DataSpec
dataSpec
;
private
final
HttpDataSource
httpDataSource
;
private
final
Parser
<
T
>
parser
;
private
volatile
T
result
;
private
volatile
T
result
;
private
volatile
boolean
isCanceled
;
private
volatile
boolean
isCanceled
;
/**
/**
* @param url The url from which the object should be loaded.
* @param url The url from which the object should be loaded.
* @param userAgent The user agent to use when requesting the object.
* @param httpDataSource A {@link HttpDataSource} to use when loading the data.
*/
* @param parser Parses the object from the network response.
public
NetworkLoadable
(
String
url
,
String
userAgent
)
{
this
(
url
,
userAgent
,
DEFAULT_TIMEOUT_MILLIS
);
}
/**
* @param url The url from which the object should be loaded.
* @param userAgent The user agent to use when requesting the object.
* @param timeoutMillis The desired http timeout in milliseconds.
*/
*/
public
NetworkLoadable
(
String
url
,
String
userAgent
,
int
timeoutMillis
)
{
public
NetworkLoadable
(
String
url
,
HttpDataSource
httpDataSource
,
Parser
<
T
>
parser
)
{
this
.
url
=
url
;
this
.
httpDataSource
=
httpDataSource
;
this
.
userAgent
=
userAgent
;
this
.
parser
=
parser
;
this
.
timeoutMillis
=
timeoutMillis
;
dataSpec
=
new
DataSpec
(
Uri
.
parse
(
url
))
;
}
}
/**
/**
...
@@ -79,41 +87,13 @@ public abstract class NetworkLoadable<T> implements Loadable {
...
@@ -79,41 +87,13 @@ public abstract class NetworkLoadable<T> implements Loadable {
@Override
@Override
public
final
void
load
()
throws
IOException
,
InterruptedException
{
public
final
void
load
()
throws
IOException
,
InterruptedException
{
String
inputEncoding
;
DataSourceInputStream
inputStream
=
new
DataSourceInputStream
(
httpDataSource
,
dataSpec
);
InputStream
inputStream
=
null
;
try
{
try
{
URLConnection
connection
=
configureConnection
(
new
URL
(
url
));
inputStream
.
open
();
inputStream
=
connection
.
getInputStream
();
result
=
parser
.
parse
(
httpDataSource
.
getUrl
(),
inputStream
);
inputEncoding
=
connection
.
getContentEncoding
();
result
=
parse
(
connection
.
getURL
().
toString
(),
inputStream
,
inputEncoding
);
}
finally
{
}
finally
{
if
(
inputStream
!=
null
)
{
inputStream
.
close
();
inputStream
.
close
();
}
}
}
}
}
/**
* Parses the raw data into an object.
*
* @param connectionUrl The url, after any redirection has taken place.
* @param inputStream An {@link InputStream} from which the raw data can be read.
* @param inputEncoding The encoding of the raw data, if available.
* @return The parsed object.
* @throws ParserException If an error occurs parsing the data.
* @throws IOException If an error occurs reading data from the stream.
*/
protected
abstract
T
parse
(
String
connectionUrl
,
InputStream
inputStream
,
String
inputEncoding
)
throws
ParserException
,
IOException
;
private
URLConnection
configureConnection
(
URL
url
)
throws
IOException
{
URLConnection
connection
=
url
.
openConnection
();
connection
.
setConnectTimeout
(
timeoutMillis
);
connection
.
setReadTimeout
(
timeoutMillis
);
connection
.
setDoOutput
(
false
);
connection
.
setRequestProperty
(
"User-Agent"
,
userAgent
);
connection
.
connect
();
return
connection
;
}
}
}
library/src/main/java/com/google/android/exoplayer/upstream/UriDataSource.java
View file @
7f70db97
...
@@ -37,14 +37,14 @@ public final class UriDataSource implements DataSource {
...
@@ -37,14 +37,14 @@ public final class UriDataSource implements DataSource {
/**
/**
* Constructs a new data source that delegates to a {@link FileDataSource} for file URIs and an
* Constructs a new data source that delegates to a {@link FileDataSource} for file URIs and an
* {@link HttpDataSource} for other URIs.
* {@link
Default
HttpDataSource} for other URIs.
*
*
* @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
UriDataSource
(
String
userAgent
,
TransferListener
transferListener
)
{
public
UriDataSource
(
String
userAgent
,
TransferListener
transferListener
)
{
this
(
new
FileDataSource
(
transferListener
),
this
(
new
FileDataSource
(
transferListener
),
new
HttpDataSource
(
userAgent
,
null
,
transferListener
));
new
Default
HttpDataSource
(
userAgent
,
null
,
transferListener
));
}
}
/**
/**
...
...
library/src/main/java/com/google/android/exoplayer/util/ManifestFetcher.java
View file @
7f70db97
...
@@ -15,9 +15,10 @@
...
@@ -15,9 +15,10 @@
*/
*/
package
com
.
google
.
android
.
exoplayer
.
util
;
package
com
.
google
.
android
.
exoplayer
.
util
;
import
com.google.android.exoplayer.
ParserException
;
import
com.google.android.exoplayer.
upstream.HttpDataSource
;
import
com.google.android.exoplayer.upstream.Loader
;
import
com.google.android.exoplayer.upstream.Loader
;
import
com.google.android.exoplayer.upstream.Loader.Loadable
;
import
com.google.android.exoplayer.upstream.Loader.Loadable
;
import
com.google.android.exoplayer.upstream.NetworkLoadable
;
import
android.os.Handler
;
import
android.os.Handler
;
import
android.os.Looper
;
import
android.os.Looper
;
...
@@ -25,11 +26,22 @@ import android.os.SystemClock;
...
@@ -25,11 +26,22 @@ import android.os.SystemClock;
import
android.util.Pair
;
import
android.util.Pair
;
import
java.io.IOException
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.util.concurrent.CancellationException
;
import
java.util.concurrent.CancellationException
;
/**
/**
* Performs both single and repeated loads of media manifests.
* Performs both single and repeated loads of media manifests.
* <p>
* Client code is responsible for ensuring that only one load is taking place at any one time.
* Typical usage of this class is as follows:
* <ol>
* <li>Create an instance.</li>
* <li>Obtain an initial manifest by calling {@link #singleLoad(Looper, ManifestCallback)} and
* waiting for the callback to be invoked.</li>
* <li>For on-demand playbacks, the loader is no longer required. For live playbacks, the loader
* may be required to periodically refresh the manifest. In this case it is injected into any
* components that require it. These components will call {@link #requestRefresh()} on the
* loader whenever a refresh is required.</li>
* </ol>
*
*
* @param <T> The type of manifest.
* @param <T> The type of manifest.
*/
*/
...
@@ -58,24 +70,21 @@ public class ManifestFetcher<T> implements Loader.Callback {
...
@@ -58,24 +70,21 @@ public class ManifestFetcher<T> implements Loader.Callback {
/**
/**
* Invoked when the load has successfully completed.
* Invoked when the load has successfully completed.
*
*
* @param contentId The content id of the media.
* @param manifest The loaded manifest.
* @param manifest The loaded manifest.
*/
*/
void
on
Manifest
(
String
contentId
,
T
manifest
);
void
on
SingleManifest
(
T
manifest
);
/**
/**
* Invoked when the load has failed.
* Invoked when the load has failed.
*
*
* @param contentId The content id of the media.
* @param e The cause of the failure.
* @param e The cause of the failure.
*/
*/
void
on
ManifestError
(
String
contentId
,
IOException
e
);
void
on
SingleManifestError
(
IOException
e
);
}
}
/* package */
final
ManifestParser
<
T
>
parser
;
private
final
NetworkLoadable
.
Parser
<
T
>
parser
;
/* package */
final
String
contentId
;
private
final
HttpDataSource
httpDataSource
;
/* package */
final
String
userAgent
;
private
final
Handler
eventHandler
;
private
final
Handler
eventHandler
;
private
final
EventListener
eventListener
;
private
final
EventListener
eventListener
;
...
@@ -83,7 +92,7 @@ public class ManifestFetcher<T> implements Loader.Callback {
...
@@ -83,7 +92,7 @@ public class ManifestFetcher<T> implements Loader.Callback {
private
int
enabledCount
;
private
int
enabledCount
;
private
Loader
loader
;
private
Loader
loader
;
private
Manifest
Loadable
<
T
>
currentLoadable
;
private
Network
Loadable
<
T
>
currentLoadable
;
private
int
loadExceptionCount
;
private
int
loadExceptionCount
;
private
long
loadExceptionTimestamp
;
private
long
loadExceptionTimestamp
;
...
@@ -92,23 +101,29 @@ public class ManifestFetcher<T> implements Loader.Callback {
...
@@ -92,23 +101,29 @@ public class ManifestFetcher<T> implements Loader.Callback {
private
volatile
T
manifest
;
private
volatile
T
manifest
;
private
volatile
long
manifestLoadTimestamp
;
private
volatile
long
manifestLoadTimestamp
;
public
ManifestFetcher
(
ManifestParser
<
T
>
parser
,
String
contentId
,
String
manifestUrl
,
/**
String
userAgent
)
{
* @param manifestUrl The manifest location.
this
(
parser
,
contentId
,
manifestUrl
,
userAgent
,
null
,
null
);
* @param httpDataSource The {@link HttpDataSource} to use when loading the manifest.
* @param parser A parser to parse the loaded manifest data.
*/
public
ManifestFetcher
(
String
manifestUrl
,
HttpDataSource
httpDataSource
,
NetworkLoadable
.
Parser
<
T
>
parser
)
{
this
(
manifestUrl
,
httpDataSource
,
parser
,
null
,
null
);
}
}
/**
/**
* @param parser A parser to parse the loaded manifest data.
* @param contentId The content id of the content being loaded. May be null.
* @param manifestUrl The manifest location.
* @param manifestUrl The manifest location.
* @param userAgent The User-Agent string that should be used.
* @param httpDataSource The {@link HttpDataSource} to use when loading the manifest.
* @param parser A parser to parse the loaded manifest data.
* @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.
*/
*/
public
ManifestFetcher
(
ManifestParser
<
T
>
parser
,
String
contentId
,
String
manifestUrl
,
public
ManifestFetcher
(
String
manifestUrl
,
HttpDataSource
httpDataSource
,
String
userAgent
,
Handler
eventHandler
,
EventListener
eventListener
)
{
NetworkLoadable
.
Parser
<
T
>
parser
,
Handler
eventHandler
,
EventListener
eventListener
)
{
this
.
parser
=
parser
;
this
.
parser
=
parser
;
this
.
contentId
=
contentId
;
this
.
manifestUrl
=
manifestUrl
;
this
.
manifestUrl
=
manifestUrl
;
this
.
userAgent
=
userAgent
;
this
.
httpDataSource
=
httpDataSource
;
this
.
eventHandler
=
eventHandler
;
this
.
eventHandler
=
eventHandler
;
this
.
eventListener
=
eventListener
;
this
.
eventListener
=
eventListener
;
}
}
...
@@ -130,7 +145,8 @@ public class ManifestFetcher<T> implements Loader.Callback {
...
@@ -130,7 +145,8 @@ public class ManifestFetcher<T> implements Loader.Callback {
* @param callback The callback to receive the result.
* @param callback The callback to receive the result.
*/
*/
public
void
singleLoad
(
Looper
callbackLooper
,
final
ManifestCallback
<
T
>
callback
)
{
public
void
singleLoad
(
Looper
callbackLooper
,
final
ManifestCallback
<
T
>
callback
)
{
SingleFetchHelper
fetchHelper
=
new
SingleFetchHelper
(
callbackLooper
,
callback
);
SingleFetchHelper
fetchHelper
=
new
SingleFetchHelper
(
new
NetworkLoadable
<
T
>(
manifestUrl
,
httpDataSource
,
parser
),
callbackLooper
,
callback
);
fetchHelper
.
startLoading
();
fetchHelper
.
startLoading
();
}
}
...
@@ -203,7 +219,7 @@ public class ManifestFetcher<T> implements Loader.Callback {
...
@@ -203,7 +219,7 @@ public class ManifestFetcher<T> implements Loader.Callback {
loader
=
new
Loader
(
"manifestLoader"
);
loader
=
new
Loader
(
"manifestLoader"
);
}
}
if
(!
loader
.
isLoading
())
{
if
(!
loader
.
isLoading
())
{
currentLoadable
=
new
ManifestLoadable
<
T
>(
manifestUrl
,
userAgent
,
contentId
,
parser
);
currentLoadable
=
new
NetworkLoadable
<
T
>(
manifestUrl
,
httpDataSource
,
parser
);
loader
.
startLoading
(
currentLoadable
,
this
);
loader
.
startLoading
(
currentLoadable
,
this
);
notifyManifestRefreshStarted
();
notifyManifestRefreshStarted
();
}
}
...
@@ -287,16 +303,17 @@ public class ManifestFetcher<T> implements Loader.Callback {
...
@@ -287,16 +303,17 @@ public class ManifestFetcher<T> implements Loader.Callback {
private
class
SingleFetchHelper
implements
Loader
.
Callback
{
private
class
SingleFetchHelper
implements
Loader
.
Callback
{
private
final
NetworkLoadable
<
T
>
singleUseLoadable
;
private
final
Looper
callbackLooper
;
private
final
Looper
callbackLooper
;
private
final
ManifestCallback
<
T
>
wrappedCallback
;
private
final
ManifestCallback
<
T
>
wrappedCallback
;
private
final
Loader
singleUseLoader
;
private
final
Loader
singleUseLoader
;
private
final
ManifestLoadable
<
T
>
singleUseLoadable
;
public
SingleFetchHelper
(
Looper
callbackLooper
,
ManifestCallback
<
T
>
wrappedCallback
)
{
public
SingleFetchHelper
(
NetworkLoadable
<
T
>
singleUseLoadable
,
Looper
callbackLooper
,
ManifestCallback
<
T
>
wrappedCallback
)
{
this
.
singleUseLoadable
=
singleUseLoadable
;
this
.
callbackLooper
=
callbackLooper
;
this
.
callbackLooper
=
callbackLooper
;
this
.
wrappedCallback
=
wrappedCallback
;
this
.
wrappedCallback
=
wrappedCallback
;
singleUseLoader
=
new
Loader
(
"manifestLoader:single"
);
singleUseLoader
=
new
Loader
(
"manifestLoader:single"
);
singleUseLoadable
=
new
ManifestLoadable
<
T
>(
manifestUrl
,
userAgent
,
contentId
,
parser
);
}
}
public
void
startLoading
()
{
public
void
startLoading
()
{
...
@@ -308,7 +325,7 @@ public class ManifestFetcher<T> implements Loader.Callback {
...
@@ -308,7 +325,7 @@ public class ManifestFetcher<T> implements Loader.Callback {
try
{
try
{
T
result
=
singleUseLoadable
.
getResult
();
T
result
=
singleUseLoadable
.
getResult
();
onSingleFetchCompleted
(
result
);
onSingleFetchCompleted
(
result
);
wrappedCallback
.
on
Manifest
(
contentId
,
result
);
wrappedCallback
.
on
SingleManifest
(
result
);
}
finally
{
}
finally
{
releaseLoader
();
releaseLoader
();
}
}
...
@@ -319,7 +336,7 @@ public class ManifestFetcher<T> implements Loader.Callback {
...
@@ -319,7 +336,7 @@ public class ManifestFetcher<T> implements Loader.Callback {
// This shouldn't ever happen, but handle it anyway.
// This shouldn't ever happen, but handle it anyway.
try
{
try
{
IOException
exception
=
new
IOException
(
"Load cancelled"
,
new
CancellationException
());
IOException
exception
=
new
IOException
(
"Load cancelled"
,
new
CancellationException
());
wrappedCallback
.
on
ManifestError
(
contentId
,
exception
);
wrappedCallback
.
on
SingleManifestError
(
exception
);
}
finally
{
}
finally
{
releaseLoader
();
releaseLoader
();
}
}
...
@@ -328,7 +345,7 @@ public class ManifestFetcher<T> implements Loader.Callback {
...
@@ -328,7 +345,7 @@ public class ManifestFetcher<T> implements Loader.Callback {
@Override
@Override
public
void
onLoadError
(
Loadable
loadable
,
IOException
exception
)
{
public
void
onLoadError
(
Loadable
loadable
,
IOException
exception
)
{
try
{
try
{
wrappedCallback
.
on
ManifestError
(
contentId
,
exception
);
wrappedCallback
.
on
SingleManifestError
(
exception
);
}
finally
{
}
finally
{
releaseLoader
();
releaseLoader
();
}
}
...
@@ -340,24 +357,4 @@ public class ManifestFetcher<T> implements Loader.Callback {
...
@@ -340,24 +357,4 @@ public class ManifestFetcher<T> implements Loader.Callback {
}
}
private
static
class
ManifestLoadable
<
T
>
extends
NetworkLoadable
<
T
>
{
private
final
String
contentId
;
private
final
ManifestParser
<
T
>
parser
;
public
ManifestLoadable
(
String
url
,
String
userAgent
,
String
contentId
,
ManifestParser
<
T
>
parser
)
{
super
(
url
,
userAgent
);
this
.
contentId
=
contentId
;
this
.
parser
=
parser
;
}
@Override
protected
T
parse
(
String
connectionUrl
,
InputStream
inputStream
,
String
inputEncoding
)
throws
ParserException
,
IOException
{
return
parser
.
parse
(
inputStream
,
inputEncoding
,
contentId
,
Util
.
parseBaseUri
(
connectionUrl
));
}
}
}
}
library/src/main/java/com/google/android/exoplayer/util/ManifestParser.java
deleted
100644 → 0
View file @
430d06d4
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
com
.
google
.
android
.
exoplayer
.
util
;
import
com.google.android.exoplayer.ParserException
;
import
android.net.Uri
;
import
java.io.IOException
;
import
java.io.InputStream
;
/**
* Parses a manifest from an {@link InputStream}.
*
* @param <T> The type of the manifest being parsed.
*/
public
interface
ManifestParser
<
T
>
{
/**
* Parses a manifest from an {@link InputStream}.
*
* @param inputStream The input stream to consume.
* @param inputEncoding The encoding of the input stream. May be null if the input encoding is
* unknown.
* @param contentId The content id to which the manifest corresponds. May be null.
* @param baseUri If the manifest contains relative uris, this is the uri they are relative to.
* May be null.
* @return The parsed manifest.
* @throws IOException If an error occurs reading the data.
* @throws ParserException If an error occurs parsing the data.
*/
T
parse
(
InputStream
inputStream
,
String
inputEncoding
,
String
contentId
,
Uri
baseUri
)
throws
IOException
,
ParserException
;
}
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