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
dd30632a
authored
Sep 25, 2014
by
ojw28
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
SmoothStreaming Live support.
Issue: #12
parent
4adf8f77
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
217 additions
and
38 deletions
demo/src/main/java/com/google/android/exoplayer/demo/DemoUtil.java
demo/src/main/java/com/google/android/exoplayer/demo/Samples.java
demo/src/main/java/com/google/android/exoplayer/demo/full/FullPlayerActivity.java
demo/src/main/java/com/google/android/exoplayer/demo/full/player/SmoothStreamingRendererBuilder.java
demo/src/main/java/com/google/android/exoplayer/demo/simple/SmoothStreamingRendererBuilder.java
library/src/main/java/com/google/android/exoplayer/BehindLiveWindowException.java
library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingChunkSource.java
demo/src/main/java/com/google/android/exoplayer/demo/DemoUtil.java
View file @
dd30632a
...
@@ -45,7 +45,7 @@ public class DemoUtil {
...
@@ -45,7 +45,7 @@ public class DemoUtil {
public
static
final
String
CONTENT_ID_EXTRA
=
"content_id"
;
public
static
final
String
CONTENT_ID_EXTRA
=
"content_id"
;
public
static
final
int
TYPE_DASH_VOD
=
0
;
public
static
final
int
TYPE_DASH_VOD
=
0
;
public
static
final
int
TYPE_SS
_VOD
=
1
;
public
static
final
int
TYPE_SS
=
1
;
public
static
final
int
TYPE_OTHER
=
2
;
public
static
final
int
TYPE_OTHER
=
2
;
public
static
final
boolean
EXPOSE_EXPERIMENTAL_FEATURES
=
false
;
public
static
final
boolean
EXPOSE_EXPERIMENTAL_FEATURES
=
false
;
...
...
demo/src/main/java/com/google/android/exoplayer/demo/Samples.java
View file @
dd30632a
...
@@ -56,7 +56,7 @@ package com.google.android.exoplayer.demo;
...
@@ -56,7 +56,7 @@ package com.google.android.exoplayer.demo;
false
),
false
),
new
Sample
(
"Super speed (SmoothStreaming)"
,
"uid:ss:superspeed"
,
new
Sample
(
"Super speed (SmoothStreaming)"
,
"uid:ss:superspeed"
,
"http://playready.directtaps.net/smoothstreaming/SSWSS720H264/SuperSpeedway_720.ism"
,
"http://playready.directtaps.net/smoothstreaming/SSWSS720H264/SuperSpeedway_720.ism"
,
DemoUtil
.
TYPE_SS
_VOD
,
false
,
false
),
DemoUtil
.
TYPE_SS
,
false
,
false
),
new
Sample
(
"Dizzy (Misc)"
,
"uid:misc:dizzy"
,
new
Sample
(
"Dizzy (Misc)"
,
"uid:misc:dizzy"
,
"http://html5demos.com/assets/dizzy.mp4"
,
DemoUtil
.
TYPE_OTHER
,
false
,
false
),
"http://html5demos.com/assets/dizzy.mp4"
,
DemoUtil
.
TYPE_OTHER
,
false
,
false
),
};
};
...
@@ -92,10 +92,10 @@ package com.google.android.exoplayer.demo;
...
@@ -92,10 +92,10 @@ package com.google.android.exoplayer.demo;
public
static
final
Sample
[]
SMOOTHSTREAMING
=
new
Sample
[]
{
public
static
final
Sample
[]
SMOOTHSTREAMING
=
new
Sample
[]
{
new
Sample
(
"Super speed"
,
"uid:ss:superspeed"
,
new
Sample
(
"Super speed"
,
"uid:ss:superspeed"
,
"http://playready.directtaps.net/smoothstreaming/SSWSS720H264/SuperSpeedway_720.ism"
,
"http://playready.directtaps.net/smoothstreaming/SSWSS720H264/SuperSpeedway_720.ism"
,
DemoUtil
.
TYPE_SS
_VOD
,
false
,
true
),
DemoUtil
.
TYPE_SS
,
false
,
true
),
new
Sample
(
"Super speed (PlayReady)"
,
"uid:ss:pr:superspeed"
,
new
Sample
(
"Super speed (PlayReady)"
,
"uid:ss:pr:superspeed"
,
"http://playready.directtaps.net/smoothstreaming/SSWSS720H264PR/SuperSpeedway_720.ism"
,
"http://playready.directtaps.net/smoothstreaming/SSWSS720H264PR/SuperSpeedway_720.ism"
,
DemoUtil
.
TYPE_SS
_VOD
,
true
,
true
),
DemoUtil
.
TYPE_SS
,
true
,
true
),
};
};
public
static
final
Sample
[]
WIDEVINE_GTS
=
new
Sample
[]
{
public
static
final
Sample
[]
WIDEVINE_GTS
=
new
Sample
[]
{
...
...
demo/src/main/java/com/google/android/exoplayer/demo/full/FullPlayerActivity.java
View file @
dd30632a
...
@@ -167,7 +167,7 @@ public class FullPlayerActivity extends Activity implements SurfaceHolder.Callba
...
@@ -167,7 +167,7 @@ public class FullPlayerActivity extends Activity implements SurfaceHolder.Callba
private
RendererBuilder
getRendererBuilder
()
{
private
RendererBuilder
getRendererBuilder
()
{
String
userAgent
=
DemoUtil
.
getUserAgent
(
this
);
String
userAgent
=
DemoUtil
.
getUserAgent
(
this
);
switch
(
contentType
)
{
switch
(
contentType
)
{
case
DemoUtil
.
TYPE_SS
_VOD
:
case
DemoUtil
.
TYPE_SS
:
return
new
SmoothStreamingRendererBuilder
(
userAgent
,
contentUri
.
toString
(),
contentId
,
return
new
SmoothStreamingRendererBuilder
(
userAgent
,
contentUri
.
toString
(),
contentId
,
new
SmoothStreamingTestMediaDrmCallback
(),
debugTextView
);
new
SmoothStreamingTestMediaDrmCallback
(),
debugTextView
);
case
DemoUtil
.
TYPE_DASH_VOD
:
case
DemoUtil
.
TYPE_DASH_VOD
:
...
...
demo/src/main/java/com/google/android/exoplayer/demo/full/player/SmoothStreamingRendererBuilder.java
View file @
dd30632a
...
@@ -65,6 +65,7 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
...
@@ -65,6 +65,7 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
private
static
final
int
VIDEO_BUFFER_SEGMENTS
=
200
;
private
static
final
int
VIDEO_BUFFER_SEGMENTS
=
200
;
private
static
final
int
AUDIO_BUFFER_SEGMENTS
=
60
;
private
static
final
int
AUDIO_BUFFER_SEGMENTS
=
60
;
private
static
final
int
TTML_BUFFER_SEGMENTS
=
2
;
private
static
final
int
TTML_BUFFER_SEGMENTS
=
2
;
private
static
final
int
LIVE_EDGE_LATENCY_MS
=
30000
;
private
final
String
userAgent
;
private
final
String
userAgent
;
private
final
String
url
;
private
final
String
url
;
...
@@ -74,6 +75,7 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
...
@@ -74,6 +75,7 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
private
DemoPlayer
player
;
private
DemoPlayer
player
;
private
RendererBuilderCallback
callback
;
private
RendererBuilderCallback
callback
;
private
ManifestFetcher
<
SmoothStreamingManifest
>
manifestFetcher
;
public
SmoothStreamingRendererBuilder
(
String
userAgent
,
String
url
,
String
contentId
,
public
SmoothStreamingRendererBuilder
(
String
userAgent
,
String
url
,
String
contentId
,
MediaDrmCallback
drmCallback
,
TextView
debugTextView
)
{
MediaDrmCallback
drmCallback
,
TextView
debugTextView
)
{
...
@@ -89,8 +91,8 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
...
@@ -89,8 +91,8 @@ 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
<
SmoothStreamingManifest
>
manifestFetcher
=
manifestFetcher
=
new
ManifestFetcher
<
SmoothStreamingManifest
>(
parser
,
contentId
,
new
ManifestFetcher
<
SmoothStreamingManifest
>(
parser
,
contentId
,
url
+
"/Manifest"
);
url
+
"/Manifest"
);
manifestFetcher
.
singleLoad
(
player
.
getMainHandler
().
getLooper
(),
this
);
manifestFetcher
.
singleLoad
(
player
.
getMainHandler
().
getLooper
(),
this
);
}
}
...
@@ -154,9 +156,9 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
...
@@ -154,9 +156,9 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
// Build the video renderer.
// Build the video renderer.
DataSource
videoDataSource
=
new
HttpDataSource
(
userAgent
,
null
,
bandwidthMeter
);
DataSource
videoDataSource
=
new
HttpDataSource
(
userAgent
,
null
,
bandwidthMeter
);
ChunkSource
videoChunkSource
=
new
SmoothStreamingChunkSource
(
manifest
,
ChunkSource
videoChunkSource
=
new
SmoothStreamingChunkSource
(
manifest
Fetcher
,
videoStreamElementIndex
,
videoTrackIndices
,
videoDataSource
,
videoStreamElementIndex
,
videoTrackIndices
,
videoDataSource
,
new
AdaptiveEvaluator
(
bandwidthMeter
));
new
AdaptiveEvaluator
(
bandwidthMeter
)
,
LIVE_EDGE_LATENCY_MS
);
ChunkSampleSource
videoSampleSource
=
new
ChunkSampleSource
(
videoChunkSource
,
loadControl
,
ChunkSampleSource
videoSampleSource
=
new
ChunkSampleSource
(
videoChunkSource
,
loadControl
,
VIDEO_BUFFER_SEGMENTS
*
BUFFER_SEGMENT_SIZE
,
true
,
mainHandler
,
player
,
VIDEO_BUFFER_SEGMENTS
*
BUFFER_SEGMENT_SIZE
,
true
,
mainHandler
,
player
,
DemoPlayer
.
TYPE_VIDEO
);
DemoPlayer
.
TYPE_VIDEO
);
...
@@ -181,8 +183,9 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
...
@@ -181,8 +183,9 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
for
(
int
i
=
0
;
i
<
manifest
.
streamElements
.
length
;
i
++)
{
for
(
int
i
=
0
;
i
<
manifest
.
streamElements
.
length
;
i
++)
{
if
(
manifest
.
streamElements
[
i
].
type
==
StreamElement
.
TYPE_AUDIO
)
{
if
(
manifest
.
streamElements
[
i
].
type
==
StreamElement
.
TYPE_AUDIO
)
{
audioTrackNames
[
audioStreamElementCount
]
=
manifest
.
streamElements
[
i
].
name
;
audioTrackNames
[
audioStreamElementCount
]
=
manifest
.
streamElements
[
i
].
name
;
audioChunkSources
[
audioStreamElementCount
]
=
new
SmoothStreamingChunkSource
(
manifest
,
audioChunkSources
[
audioStreamElementCount
]
=
new
SmoothStreamingChunkSource
(
i
,
new
int
[]
{
0
},
audioDataSource
,
audioFormatEvaluator
);
manifestFetcher
,
i
,
new
int
[]
{
0
},
audioDataSource
,
audioFormatEvaluator
,
LIVE_EDGE_LATENCY_MS
);
audioStreamElementCount
++;
audioStreamElementCount
++;
}
}
}
}
...
@@ -211,8 +214,8 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
...
@@ -211,8 +214,8 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
for
(
int
i
=
0
;
i
<
manifest
.
streamElements
.
length
;
i
++)
{
for
(
int
i
=
0
;
i
<
manifest
.
streamElements
.
length
;
i
++)
{
if
(
manifest
.
streamElements
[
i
].
type
==
StreamElement
.
TYPE_TEXT
)
{
if
(
manifest
.
streamElements
[
i
].
type
==
StreamElement
.
TYPE_TEXT
)
{
textTrackNames
[
textStreamElementCount
]
=
manifest
.
streamElements
[
i
].
language
;
textTrackNames
[
textStreamElementCount
]
=
manifest
.
streamElements
[
i
].
language
;
textChunkSources
[
textStreamElementCount
]
=
new
SmoothStreamingChunkSource
(
manifest
,
textChunkSources
[
textStreamElementCount
]
=
new
SmoothStreamingChunkSource
(
manifest
Fetcher
,
i
,
new
int
[]
{
0
},
ttmlDataSource
,
ttmlFormatEvaluator
);
i
,
new
int
[]
{
0
},
ttmlDataSource
,
ttmlFormatEvaluator
,
LIVE_EDGE_LATENCY_MS
);
textStreamElementCount
++;
textStreamElementCount
++;
}
}
}
}
...
...
demo/src/main/java/com/google/android/exoplayer/demo/simple/SmoothStreamingRendererBuilder.java
View file @
dd30632a
...
@@ -54,6 +54,7 @@ import java.util.ArrayList;
...
@@ -54,6 +54,7 @@ import java.util.ArrayList;
private
static
final
int
BUFFER_SEGMENT_SIZE
=
64
*
1024
;
private
static
final
int
BUFFER_SEGMENT_SIZE
=
64
*
1024
;
private
static
final
int
VIDEO_BUFFER_SEGMENTS
=
200
;
private
static
final
int
VIDEO_BUFFER_SEGMENTS
=
200
;
private
static
final
int
AUDIO_BUFFER_SEGMENTS
=
60
;
private
static
final
int
AUDIO_BUFFER_SEGMENTS
=
60
;
private
static
final
int
LIVE_EDGE_LATENCY_MS
=
30000
;
private
final
SimplePlayerActivity
playerActivity
;
private
final
SimplePlayerActivity
playerActivity
;
private
final
String
userAgent
;
private
final
String
userAgent
;
...
@@ -61,6 +62,7 @@ import java.util.ArrayList;
...
@@ -61,6 +62,7 @@ import java.util.ArrayList;
private
final
String
contentId
;
private
final
String
contentId
;
private
RendererBuilderCallback
callback
;
private
RendererBuilderCallback
callback
;
private
ManifestFetcher
<
SmoothStreamingManifest
>
manifestFetcher
;
public
SmoothStreamingRendererBuilder
(
SimplePlayerActivity
playerActivity
,
String
userAgent
,
public
SmoothStreamingRendererBuilder
(
SimplePlayerActivity
playerActivity
,
String
userAgent
,
String
url
,
String
contentId
)
{
String
url
,
String
contentId
)
{
...
@@ -74,8 +76,8 @@ import java.util.ArrayList;
...
@@ -74,8 +76,8 @@ import java.util.ArrayList;
public
void
buildRenderers
(
RendererBuilderCallback
callback
)
{
public
void
buildRenderers
(
RendererBuilderCallback
callback
)
{
this
.
callback
=
callback
;
this
.
callback
=
callback
;
SmoothStreamingManifestParser
parser
=
new
SmoothStreamingManifestParser
();
SmoothStreamingManifestParser
parser
=
new
SmoothStreamingManifestParser
();
ManifestFetcher
<
SmoothStreamingManifest
>
manifestFetcher
=
manifestFetcher
=
new
ManifestFetcher
<
SmoothStreamingManifest
>(
parser
,
contentId
,
new
ManifestFetcher
<
SmoothStreamingManifest
>(
parser
,
contentId
,
url
+
"/Manifest"
);
url
+
"/Manifest"
);
manifestFetcher
.
singleLoad
(
playerActivity
.
getMainLooper
(),
this
);
manifestFetcher
.
singleLoad
(
playerActivity
.
getMainLooper
(),
this
);
}
}
...
@@ -120,8 +122,9 @@ import java.util.ArrayList;
...
@@ -120,8 +122,9 @@ import java.util.ArrayList;
// Build the video renderer.
// Build the video renderer.
DataSource
videoDataSource
=
new
HttpDataSource
(
userAgent
,
null
,
bandwidthMeter
);
DataSource
videoDataSource
=
new
HttpDataSource
(
userAgent
,
null
,
bandwidthMeter
);
ChunkSource
videoChunkSource
=
new
SmoothStreamingChunkSource
(
manifest
,
videoStreamElementIndex
,
ChunkSource
videoChunkSource
=
new
SmoothStreamingChunkSource
(
manifestFetcher
,
videoTrackIndices
,
videoDataSource
,
new
AdaptiveEvaluator
(
bandwidthMeter
));
videoStreamElementIndex
,
videoTrackIndices
,
videoDataSource
,
new
AdaptiveEvaluator
(
bandwidthMeter
),
LIVE_EDGE_LATENCY_MS
);
ChunkSampleSource
videoSampleSource
=
new
ChunkSampleSource
(
videoChunkSource
,
loadControl
,
ChunkSampleSource
videoSampleSource
=
new
ChunkSampleSource
(
videoChunkSource
,
loadControl
,
VIDEO_BUFFER_SEGMENTS
*
BUFFER_SEGMENT_SIZE
,
true
);
VIDEO_BUFFER_SEGMENTS
*
BUFFER_SEGMENT_SIZE
,
true
);
MediaCodecVideoTrackRenderer
videoRenderer
=
new
MediaCodecVideoTrackRenderer
(
videoSampleSource
,
MediaCodecVideoTrackRenderer
videoRenderer
=
new
MediaCodecVideoTrackRenderer
(
videoSampleSource
,
...
@@ -129,8 +132,9 @@ import java.util.ArrayList;
...
@@ -129,8 +132,9 @@ import java.util.ArrayList;
// Build the audio renderer.
// Build the audio renderer.
DataSource
audioDataSource
=
new
HttpDataSource
(
userAgent
,
null
,
bandwidthMeter
);
DataSource
audioDataSource
=
new
HttpDataSource
(
userAgent
,
null
,
bandwidthMeter
);
ChunkSource
audioChunkSource
=
new
SmoothStreamingChunkSource
(
manifest
,
audioStreamElementIndex
,
ChunkSource
audioChunkSource
=
new
SmoothStreamingChunkSource
(
manifestFetcher
,
new
int
[]
{
0
},
audioDataSource
,
new
FormatEvaluator
.
FixedEvaluator
());
audioStreamElementIndex
,
new
int
[]
{
0
},
audioDataSource
,
new
FormatEvaluator
.
FixedEvaluator
(),
LIVE_EDGE_LATENCY_MS
);
SampleSource
audioSampleSource
=
new
ChunkSampleSource
(
audioChunkSource
,
loadControl
,
SampleSource
audioSampleSource
=
new
ChunkSampleSource
(
audioChunkSource
,
loadControl
,
AUDIO_BUFFER_SEGMENTS
*
BUFFER_SEGMENT_SIZE
,
true
);
AUDIO_BUFFER_SEGMENTS
*
BUFFER_SEGMENT_SIZE
,
true
);
MediaCodecAudioTrackRenderer
audioRenderer
=
new
MediaCodecAudioTrackRenderer
(
MediaCodecAudioTrackRenderer
audioRenderer
=
new
MediaCodecAudioTrackRenderer
(
...
...
library/src/main/java/com/google/android/exoplayer/BehindLiveWindowException.java
0 → 100644
View file @
dd30632a
/*
* 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
;
import
java.io.IOException
;
/**
* Thrown when a live playback falls behind the available media window.
*/
public
class
BehindLiveWindowException
extends
IOException
{
public
BehindLiveWindowException
()
{
super
();
}
public
BehindLiveWindowException
(
String
message
)
{
super
(
message
);
}
}
library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingChunkSource.java
View file @
dd30632a
...
@@ -15,6 +15,7 @@
...
@@ -15,6 +15,7 @@
*/
*/
package
com
.
google
.
android
.
exoplayer
.
smoothstreaming
;
package
com
.
google
.
android
.
exoplayer
.
smoothstreaming
;
import
com.google.android.exoplayer.BehindLiveWindowException
;
import
com.google.android.exoplayer.MediaFormat
;
import
com.google.android.exoplayer.MediaFormat
;
import
com.google.android.exoplayer.TrackInfo
;
import
com.google.android.exoplayer.TrackInfo
;
import
com.google.android.exoplayer.chunk.Chunk
;
import
com.google.android.exoplayer.chunk.Chunk
;
...
@@ -36,8 +37,10 @@ import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest.Trac
...
@@ -36,8 +37,10 @@ import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest.Trac
import
com.google.android.exoplayer.upstream.DataSource
;
import
com.google.android.exoplayer.upstream.DataSource
;
import
com.google.android.exoplayer.upstream.DataSpec
;
import
com.google.android.exoplayer.upstream.DataSpec
;
import
com.google.android.exoplayer.util.CodecSpecificDataUtil
;
import
com.google.android.exoplayer.util.CodecSpecificDataUtil
;
import
com.google.android.exoplayer.util.ManifestFetcher
;
import
android.net.Uri
;
import
android.net.Uri
;
import
android.os.SystemClock
;
import
android.util.Base64
;
import
android.util.Base64
;
import
android.util.SparseArray
;
import
android.util.SparseArray
;
...
@@ -51,13 +54,16 @@ import java.util.List;
...
@@ -51,13 +54,16 @@ import java.util.List;
*/
*/
public
class
SmoothStreamingChunkSource
implements
ChunkSource
{
public
class
SmoothStreamingChunkSource
implements
ChunkSource
{
private
static
final
int
MINIMUM_MANIFEST_REFRESH_PERIOD_MS
=
5000
;
private
static
final
int
INITIALIZATION_VECTOR_SIZE
=
8
;
private
static
final
int
INITIALIZATION_VECTOR_SIZE
=
8
;
private
final
StreamElement
streamElement
;
private
final
ManifestFetcher
<
SmoothStreamingManifest
>
manifestFetcher
;
private
final
int
streamElementIndex
;
private
final
TrackInfo
trackInfo
;
private
final
TrackInfo
trackInfo
;
private
final
DataSource
dataSource
;
private
final
DataSource
dataSource
;
private
final
FormatEvaluator
formatEvaluator
;
private
final
FormatEvaluator
formatEvaluator
;
private
final
Evaluation
evaluation
;
private
final
Evaluation
evaluation
;
private
final
long
liveEdgeLatencyUs
;
private
final
int
maxWidth
;
private
final
int
maxWidth
;
private
final
int
maxHeight
;
private
final
int
maxHeight
;
...
@@ -65,7 +71,42 @@ public class SmoothStreamingChunkSource implements ChunkSource {
...
@@ -65,7 +71,42 @@ public class SmoothStreamingChunkSource implements ChunkSource {
private
final
SparseArray
<
FragmentedMp4Extractor
>
extractors
;
private
final
SparseArray
<
FragmentedMp4Extractor
>
extractors
;
private
final
SmoothStreamingFormat
[]
formats
;
private
final
SmoothStreamingFormat
[]
formats
;
private
SmoothStreamingManifest
currentManifest
;
private
int
currentManifestChunkOffset
;
private
boolean
finishedCurrentManifest
;
private
IOException
fatalError
;
/**
* Constructor to use for live streaming.
* <p>
* May also be used for fixed duration content, in which case the call is equivalent to calling
* the other constructor, passing {@code manifestFetcher.getManifest()} is the first argument.
*
* @param manifestFetcher A fetcher for the manifest, which must have already successfully
* completed an initial load.
* @param streamElementIndex The index of the stream element in the manifest to be provided by
* the source.
* @param trackIndices The indices of the tracks within the stream element to be considered by
* the source. May be null if all tracks within the element should be considered.
* @param dataSource A {@link DataSource} suitable for loading the media data.
* @param formatEvaluator Selects from the available formats.
* @param liveEdgeLatencyMs For live streams, the number of milliseconds that the playback should
* lag behind the "live edge" (i.e. the end of the most recently defined media in the
* manifest). Choosing a small value will minimize latency introduced by the player, however
* note that the value sets an upper bound on the length of media that the player can buffer.
* Hence a small value may increase the probability of rebuffering and playback failures.
*/
public
SmoothStreamingChunkSource
(
ManifestFetcher
<
SmoothStreamingManifest
>
manifestFetcher
,
int
streamElementIndex
,
int
[]
trackIndices
,
DataSource
dataSource
,
FormatEvaluator
formatEvaluator
,
long
liveEdgeLatencyMs
)
{
this
(
manifestFetcher
,
manifestFetcher
.
getManifest
(),
streamElementIndex
,
trackIndices
,
dataSource
,
formatEvaluator
,
liveEdgeLatencyMs
);
}
/**
/**
* Constructor to use for fixed duration content.
*
* @param manifest The manifest parsed from {@code baseUrl + "/Manifest"}.
* @param manifest The manifest parsed from {@code baseUrl + "/Manifest"}.
* @param streamElementIndex The index of the stream element in the manifest to be provided by
* @param streamElementIndex The index of the stream element in the manifest to be provided by
* the source.
* the source.
...
@@ -76,14 +117,25 @@ public class SmoothStreamingChunkSource implements ChunkSource {
...
@@ -76,14 +117,25 @@ public class SmoothStreamingChunkSource implements ChunkSource {
*/
*/
public
SmoothStreamingChunkSource
(
SmoothStreamingManifest
manifest
,
int
streamElementIndex
,
public
SmoothStreamingChunkSource
(
SmoothStreamingManifest
manifest
,
int
streamElementIndex
,
int
[]
trackIndices
,
DataSource
dataSource
,
FormatEvaluator
formatEvaluator
)
{
int
[]
trackIndices
,
DataSource
dataSource
,
FormatEvaluator
formatEvaluator
)
{
this
.
streamElement
=
manifest
.
streamElements
[
streamElementIndex
];
this
(
null
,
manifest
,
streamElementIndex
,
trackIndices
,
dataSource
,
formatEvaluator
,
0
);
this
.
trackInfo
=
new
TrackInfo
(
streamElement
.
tracks
[
0
].
mimeType
,
manifest
.
durationUs
);
}
private
SmoothStreamingChunkSource
(
ManifestFetcher
<
SmoothStreamingManifest
>
manifestFetcher
,
SmoothStreamingManifest
initialManifest
,
int
streamElementIndex
,
int
[]
trackIndices
,
DataSource
dataSource
,
FormatEvaluator
formatEvaluator
,
long
liveEdgeLatencyMs
)
{
this
.
manifestFetcher
=
manifestFetcher
;
this
.
streamElementIndex
=
streamElementIndex
;
this
.
currentManifest
=
initialManifest
;
this
.
dataSource
=
dataSource
;
this
.
dataSource
=
dataSource
;
this
.
formatEvaluator
=
formatEvaluator
;
this
.
formatEvaluator
=
formatEvaluator
;
this
.
evaluation
=
new
Evaluation
();
this
.
liveEdgeLatencyUs
=
liveEdgeLatencyMs
*
1000
;
StreamElement
streamElement
=
getElement
(
initialManifest
);
trackInfo
=
new
TrackInfo
(
streamElement
.
tracks
[
0
].
mimeType
,
initialManifest
.
durationUs
);
evaluation
=
new
Evaluation
();
TrackEncryptionBox
[]
trackEncryptionBoxes
=
null
;
TrackEncryptionBox
[]
trackEncryptionBoxes
=
null
;
ProtectionElement
protectionElement
=
m
anifest
.
protectionElement
;
ProtectionElement
protectionElement
=
initialM
anifest
.
protectionElement
;
if
(
protectionElement
!=
null
)
{
if
(
protectionElement
!=
null
)
{
byte
[]
keyId
=
getKeyId
(
protectionElement
.
data
);
byte
[]
keyId
=
getKeyId
(
protectionElement
.
data
);
trackEncryptionBoxes
=
new
TrackEncryptionBox
[
1
];
trackEncryptionBoxes
=
new
TrackEncryptionBox
[
1
];
...
@@ -135,22 +187,52 @@ public class SmoothStreamingChunkSource implements ChunkSource {
...
@@ -135,22 +187,52 @@ public class SmoothStreamingChunkSource implements ChunkSource {
@Override
@Override
public
void
enable
()
{
public
void
enable
()
{
// Do nothing.
fatalError
=
null
;
if
(
manifestFetcher
!=
null
)
{
manifestFetcher
.
enable
();
}
}
}
@Override
@Override
public
void
disable
(
List
<?
extends
MediaChunk
>
queue
)
{
public
void
disable
(
List
<?
extends
MediaChunk
>
queue
)
{
// Do nothing.
if
(
manifestFetcher
!=
null
)
{
manifestFetcher
.
disable
();
}
}
}
@Override
@Override
public
void
continueBuffering
(
long
playbackPositionUs
)
{
public
void
continueBuffering
(
long
playbackPositionUs
)
{
// Do nothing
if
(
manifestFetcher
==
null
||
!
currentManifest
.
isLive
||
fatalError
!=
null
)
{
return
;
}
SmoothStreamingManifest
newManifest
=
manifestFetcher
.
getManifest
();
if
(
currentManifest
!=
newManifest
&&
newManifest
!=
null
)
{
StreamElement
currentElement
=
getElement
(
currentManifest
);
StreamElement
newElement
=
getElement
(
newManifest
);
if
(
newElement
.
chunkCount
==
0
)
{
currentManifestChunkOffset
+=
currentElement
.
chunkCount
;
}
else
if
(
currentElement
.
chunkCount
>
0
)
{
currentManifestChunkOffset
+=
currentElement
.
getChunkIndex
(
newElement
.
getStartTimeUs
(
0
));
}
currentManifest
=
newManifest
;
finishedCurrentManifest
=
false
;
}
if
(
finishedCurrentManifest
&&
(
SystemClock
.
elapsedRealtime
()
>
manifestFetcher
.
getManifestLoadTimestamp
()
+
MINIMUM_MANIFEST_REFRESH_PERIOD_MS
))
{
manifestFetcher
.
requestRefresh
();
}
}
}
@Override
@Override
public
final
void
getChunkOperation
(
List
<?
extends
MediaChunk
>
queue
,
long
seekPositionUs
,
public
final
void
getChunkOperation
(
List
<?
extends
MediaChunk
>
queue
,
long
seekPositionUs
,
long
playbackPositionUs
,
ChunkOperationHolder
out
)
{
long
playbackPositionUs
,
ChunkOperationHolder
out
)
{
if
(
fatalError
!=
null
)
{
out
.
chunk
=
null
;
return
;
}
evaluation
.
queueSize
=
queue
.
size
();
evaluation
.
queueSize
=
queue
.
size
();
formatEvaluator
.
evaluate
(
queue
,
playbackPositionUs
,
formats
,
evaluation
);
formatEvaluator
.
evaluate
(
queue
,
playbackPositionUs
,
formats
,
evaluation
);
SmoothStreamingFormat
selectedFormat
=
(
SmoothStreamingFormat
)
evaluation
.
format
;
SmoothStreamingFormat
selectedFormat
=
(
SmoothStreamingFormat
)
evaluation
.
format
;
...
@@ -166,30 +248,63 @@ public class SmoothStreamingChunkSource implements ChunkSource {
...
@@ -166,30 +248,63 @@ public class SmoothStreamingChunkSource implements ChunkSource {
return
;
return
;
}
}
int
nextChunkIndex
;
// In all cases where we return before instantiating a new chunk at the bottom of this method,
// we want out.chunk to be null.
out
.
chunk
=
null
;
StreamElement
streamElement
=
getElement
(
currentManifest
);
if
(
streamElement
.
chunkCount
==
0
)
{
// The manifest is currently empty for this stream.
finishedCurrentManifest
=
true
;
return
;
}
int
chunkIndex
;
if
(
queue
.
isEmpty
())
{
if
(
queue
.
isEmpty
())
{
nextChunkIndex
=
streamElement
.
getChunkIndex
(
seekPositionUs
);
if
(
currentManifest
.
isLive
)
{
seekPositionUs
=
getLiveSeekPosition
();
}
chunkIndex
=
streamElement
.
getChunkIndex
(
seekPositionUs
);
}
else
{
}
else
{
nextChunkIndex
=
queue
.
get
(
out
.
queueSize
-
1
).
nextChunkIndex
;
chunkIndex
=
queue
.
get
(
out
.
queueSize
-
1
).
nextChunkIndex
-
currentManifestChunkOffset
;
}
}
if
(
nextChunkIndex
==
-
1
)
{
if
(
currentManifest
.
isLive
)
{
out
.
chunk
=
null
;
if
(
chunkIndex
<
0
)
{
// This is before the first chunk in the current manifest.
fatalError
=
new
BehindLiveWindowException
();
return
;
}
else
if
(
chunkIndex
>=
streamElement
.
chunkCount
)
{
// This is beyond the last chunk in the current manifest.
finishedCurrentManifest
=
true
;
return
;
}
else
if
(
chunkIndex
==
streamElement
.
chunkCount
-
1
)
{
// This is the last chunk in the current manifest. Mark the manifest as being finished,
// but continue to return the final chunk.
finishedCurrentManifest
=
true
;
}
}
else
if
(
chunkIndex
==
-
1
)
{
// We've reached the end of the stream.
return
;
return
;
}
}
boolean
isLastChunk
=
nextChunkIndex
==
streamElement
.
chunkCount
-
1
;
boolean
isLastChunk
=
!
currentManifest
.
isLive
&&
chunkIndex
==
streamElement
.
chunkCount
-
1
;
Uri
uri
=
streamElement
.
buildRequestUri
(
selectedFormat
.
trackIndex
,
nextChunkIndex
);
long
chunkStartTimeUs
=
streamElement
.
getStartTimeUs
(
chunkIndex
);
long
nextChunkStartTimeUs
=
isLastChunk
?
-
1
:
chunkStartTimeUs
+
streamElement
.
getChunkDurationUs
(
chunkIndex
);
int
currentAbsoluteChunkIndex
=
chunkIndex
+
currentManifestChunkOffset
;
Uri
uri
=
streamElement
.
buildRequestUri
(
selectedFormat
.
trackIndex
,
chunkIndex
);
Chunk
mediaChunk
=
newMediaChunk
(
selectedFormat
,
uri
,
null
,
Chunk
mediaChunk
=
newMediaChunk
(
selectedFormat
,
uri
,
null
,
extractors
.
get
(
Integer
.
parseInt
(
selectedFormat
.
id
)),
dataSource
,
nextChunkIndex
,
extractors
.
get
(
Integer
.
parseInt
(
selectedFormat
.
id
)),
dataSource
,
currentAbsoluteChunkIndex
,
isLastChunk
,
streamElement
.
getStartTimeUs
(
nextChunkIndex
),
isLastChunk
,
chunkStartTimeUs
,
nextChunkStartTimeUs
,
0
);
isLastChunk
?
-
1
:
streamElement
.
getStartTimeUs
(
nextChunkIndex
+
1
),
0
);
out
.
chunk
=
mediaChunk
;
out
.
chunk
=
mediaChunk
;
}
}
@Override
@Override
public
IOException
getError
()
{
public
IOException
getError
()
{
return
null
;
return
fatalError
!=
null
?
fatalError
:
(
manifestFetcher
!=
null
?
manifestFetcher
.
getError
()
:
null
);
}
}
@Override
@Override
...
@@ -197,6 +312,30 @@ public class SmoothStreamingChunkSource implements ChunkSource {
...
@@ -197,6 +312,30 @@ public class SmoothStreamingChunkSource implements ChunkSource {
// Do nothing.
// Do nothing.
}
}
/**
* For live playbacks, determines the seek position that snaps playback to be
* {@link #liveEdgeLatencyUs} behind the live edge of the current manifest
*
* @return The seek position in microseconds.
*/
private
long
getLiveSeekPosition
()
{
long
liveEdgeTimestampUs
=
Long
.
MIN_VALUE
;
for
(
int
i
=
0
;
i
<
currentManifest
.
streamElements
.
length
;
i
++)
{
StreamElement
streamElement
=
currentManifest
.
streamElements
[
i
];
if
(
streamElement
.
chunkCount
>
0
)
{
long
elementLiveEdgeTimestampUs
=
streamElement
.
getStartTimeUs
(
streamElement
.
chunkCount
-
1
)
+
streamElement
.
getChunkDurationUs
(
streamElement
.
chunkCount
-
1
);
liveEdgeTimestampUs
=
Math
.
max
(
liveEdgeTimestampUs
,
elementLiveEdgeTimestampUs
);
}
}
return
liveEdgeTimestampUs
-
liveEdgeLatencyUs
;
}
private
StreamElement
getElement
(
SmoothStreamingManifest
manifest
)
{
return
manifest
.
streamElements
[
streamElementIndex
];
}
private
static
MediaFormat
getMediaFormat
(
StreamElement
streamElement
,
int
trackIndex
)
{
private
static
MediaFormat
getMediaFormat
(
StreamElement
streamElement
,
int
trackIndex
)
{
TrackElement
trackElement
=
streamElement
.
tracks
[
trackIndex
];
TrackElement
trackElement
=
streamElement
.
tracks
[
trackIndex
];
String
mimeType
=
trackElement
.
mimeType
;
String
mimeType
=
trackElement
.
mimeType
;
...
...
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