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
511dd943
authored
Dec 03, 2014
by
Oliver Woodman
Browse files
Options
_('Browse Files')
Download
Plain Diff
Merge branch 'dev' into dev-hls
parents
c2d55aca
4efc0abd
Show whitespace changes
Inline
Side-by-side
Showing
25 changed files
with
731 additions
and
213 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/DashVodRendererBuilder.java → demo/src/main/java/com/google/android/exoplayer/demo/full/player/DashRendererBuilder.java
demo/src/main/java/com/google/android/exoplayer/demo/full/player/SmoothStreamingRendererBuilder.java
demo/src/main/java/com/google/android/exoplayer/demo/simple/DashVodRendererBuilder.java → demo/src/main/java/com/google/android/exoplayer/demo/simple/DashRendererBuilder.java
demo/src/main/java/com/google/android/exoplayer/demo/simple/SimplePlayerActivity.java
demo/src/main/java/com/google/android/exoplayer/demo/simple/SmoothStreamingRendererBuilder.java
library/src/main/java/com/google/android/exoplayer/C.java
library/src/main/java/com/google/android/exoplayer/FrameworkSampleSource.java
library/src/main/java/com/google/android/exoplayer/TrackInfo.java
library/src/main/java/com/google/android/exoplayer/TrackRenderer.java
library/src/main/java/com/google/android/exoplayer/audio/AudioCapabilitiesReceiver.java
library/src/main/java/com/google/android/exoplayer/audio/AudioTrack.java
library/src/main/java/com/google/android/exoplayer/chunk/MultiTrackChunkSource.java
library/src/main/java/com/google/android/exoplayer/chunk/SingleSampleChunkSource.java
library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java
library/src/main/java/com/google/android/exoplayer/dash/mpd/Period.java
library/src/main/java/com/google/android/exoplayer/dash/mpd/SegmentBase.java
library/src/main/java/com/google/android/exoplayer/parser/mp4/FragmentedMp4Extractor.java
library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingManifest.java
library/src/main/java/com/google/android/exoplayer/text/ttml/TtmlParser.java
library/src/main/java/com/google/android/exoplayer/util/ManifestFetcher.java
library/src/main/java/com/google/android/exoplayer/util/PlayerControl.java
library/src/main/java/com/google/android/exoplayer/util/Util.java
demo/src/main/java/com/google/android/exoplayer/demo/DemoUtil.java
View file @
511dd943
...
@@ -47,7 +47,7 @@ public class DemoUtil {
...
@@ -47,7 +47,7 @@ public class DemoUtil {
public
static
final
String
CONTENT_TYPE_EXTRA
=
"content_type"
;
public
static
final
String
CONTENT_TYPE_EXTRA
=
"content_type"
;
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
=
0
;
public
static
final
int
TYPE_SS
=
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
int
TYPE_DASH_LIVE
=
3
;
public
static
final
int
TYPE_DASH_LIVE
=
3
;
...
...
demo/src/main/java/com/google/android/exoplayer/demo/Samples.java
View file @
511dd943
...
@@ -46,13 +46,13 @@ package com.google.android.exoplayer.demo;
...
@@ -46,13 +46,13 @@ package com.google.android.exoplayer.demo;
"http://www.youtube.com/api/manifest/dash/id/bf5bb2419360daf1/source/youtube?"
"http://www.youtube.com/api/manifest/dash/id/bf5bb2419360daf1/source/youtube?"
+
"as=fmp4_audio_clear,fmp4_sd_hd_clear&sparams=ip,ipbits,expire,as&ip=0.0.0.0&"
+
"as=fmp4_audio_clear,fmp4_sd_hd_clear&sparams=ip,ipbits,expire,as&ip=0.0.0.0&"
+
"ipbits=0&expire=19000000000&signature=255F6B3C07C753C88708C07EA31B7A1A10703C8D."
+
"ipbits=0&expire=19000000000&signature=255F6B3C07C753C88708C07EA31B7A1A10703C8D."
+
"2D6A28B21F921D0B245CDCF36F7EB54A2B5ABFC2&key=ik0"
,
DemoUtil
.
TYPE_DASH
_VOD
,
false
,
+
"2D6A28B21F921D0B245CDCF36F7EB54A2B5ABFC2&key=ik0"
,
DemoUtil
.
TYPE_DASH
,
false
,
false
),
false
),
new
Sample
(
"Google Play (DASH)"
,
"3aa39fa2cc27967f"
,
new
Sample
(
"Google Play (DASH)"
,
"3aa39fa2cc27967f"
,
"http://www.youtube.com/api/manifest/dash/id/3aa39fa2cc27967f/source/youtube?"
"http://www.youtube.com/api/manifest/dash/id/3aa39fa2cc27967f/source/youtube?"
+
"as=fmp4_audio_clear,fmp4_sd_hd_clear&sparams=ip,ipbits,expire,as&ip=0.0.0.0&ipbits=0&"
+
"as=fmp4_audio_clear,fmp4_sd_hd_clear&sparams=ip,ipbits,expire,as&ip=0.0.0.0&ipbits=0&"
+
"expire=19000000000&signature=7181C59D0252B285D593E1B61D985D5B7C98DE2A."
+
"expire=19000000000&signature=7181C59D0252B285D593E1B61D985D5B7C98DE2A."
+
"5B445837F55A40E0F28AACAA047982E372D177E2&key=ik0"
,
DemoUtil
.
TYPE_DASH
_VOD
,
false
,
+
"5B445837F55A40E0F28AACAA047982E372D177E2&key=ik0"
,
DemoUtil
.
TYPE_DASH
,
false
,
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"
,
...
@@ -69,13 +69,13 @@ package com.google.android.exoplayer.demo;
...
@@ -69,13 +69,13 @@ package com.google.android.exoplayer.demo;
"http://www.youtube.com/api/manifest/dash/id/bf5bb2419360daf1/source/youtube?"
"http://www.youtube.com/api/manifest/dash/id/bf5bb2419360daf1/source/youtube?"
+
"as=fmp4_audio_clear,fmp4_sd_hd_clear&sparams=ip,ipbits,expire,as&ip=0.0.0.0&"
+
"as=fmp4_audio_clear,fmp4_sd_hd_clear&sparams=ip,ipbits,expire,as&ip=0.0.0.0&"
+
"ipbits=0&expire=19000000000&signature=255F6B3C07C753C88708C07EA31B7A1A10703C8D."
+
"ipbits=0&expire=19000000000&signature=255F6B3C07C753C88708C07EA31B7A1A10703C8D."
+
"2D6A28B21F921D0B245CDCF36F7EB54A2B5ABFC2&key=ik0"
,
DemoUtil
.
TYPE_DASH
_VOD
,
false
,
+
"2D6A28B21F921D0B245CDCF36F7EB54A2B5ABFC2&key=ik0"
,
DemoUtil
.
TYPE_DASH
,
false
,
true
),
true
),
new
Sample
(
"Google Play"
,
"3aa39fa2cc27967f"
,
new
Sample
(
"Google Play"
,
"3aa39fa2cc27967f"
,
"http://www.youtube.com/api/manifest/dash/id/3aa39fa2cc27967f/source/youtube?"
"http://www.youtube.com/api/manifest/dash/id/3aa39fa2cc27967f/source/youtube?"
+
"as=fmp4_audio_clear,fmp4_sd_hd_clear&sparams=ip,ipbits,expire,as&ip=0.0.0.0&ipbits=0&"
+
"as=fmp4_audio_clear,fmp4_sd_hd_clear&sparams=ip,ipbits,expire,as&ip=0.0.0.0&ipbits=0&"
+
"expire=19000000000&signature=7181C59D0252B285D593E1B61D985D5B7C98DE2A."
+
"expire=19000000000&signature=7181C59D0252B285D593E1B61D985D5B7C98DE2A."
+
"5B445837F55A40E0F28AACAA047982E372D177E2&key=ik0"
,
DemoUtil
.
TYPE_DASH
_VOD
,
false
,
+
"5B445837F55A40E0F28AACAA047982E372D177E2&key=ik0"
,
DemoUtil
.
TYPE_DASH
,
false
,
true
),
true
),
};
};
...
@@ -84,12 +84,12 @@ package com.google.android.exoplayer.demo;
...
@@ -84,12 +84,12 @@ package com.google.android.exoplayer.demo;
"http://www.youtube.com/api/manifest/dash/id/bf5bb2419360daf1/source/youtube?"
"http://www.youtube.com/api/manifest/dash/id/bf5bb2419360daf1/source/youtube?"
+
"as=fmp4_audio_clear,webm2_sd_hd_clear&sparams=ip,ipbits,expire,as&ip=0.0.0.0&ipbits=0&"
+
"as=fmp4_audio_clear,webm2_sd_hd_clear&sparams=ip,ipbits,expire,as&ip=0.0.0.0&ipbits=0&"
+
"expire=19000000000&signature=A3EC7EE53ABE601B357F7CAB8B54AD0702CA85A7."
+
"expire=19000000000&signature=A3EC7EE53ABE601B357F7CAB8B54AD0702CA85A7."
+
"446E9C38E47E3EDAF39E0163C390FF83A7944918&key=ik0"
,
DemoUtil
.
TYPE_DASH
_VOD
,
false
,
true
),
+
"446E9C38E47E3EDAF39E0163C390FF83A7944918&key=ik0"
,
DemoUtil
.
TYPE_DASH
,
false
,
true
),
new
Sample
(
"Google Play"
,
"3aa39fa2cc27967f"
,
new
Sample
(
"Google Play"
,
"3aa39fa2cc27967f"
,
"http://www.youtube.com/api/manifest/dash/id/3aa39fa2cc27967f/source/youtube?"
"http://www.youtube.com/api/manifest/dash/id/3aa39fa2cc27967f/source/youtube?"
+
"as=fmp4_audio_clear,webm2_sd_hd_clear&sparams=ip,ipbits,expire,as&ip=0.0.0.0&ipbits=0&"
+
"as=fmp4_audio_clear,webm2_sd_hd_clear&sparams=ip,ipbits,expire,as&ip=0.0.0.0&ipbits=0&"
+
"expire=19000000000&signature=B752B262C6D7262EC4E4EB67901E5D8F7058A81D."
+
"expire=19000000000&signature=B752B262C6D7262EC4E4EB67901E5D8F7058A81D."
+
"C0358CE1E335417D9A8D88FF192F0D5D8F6DA1B6&key=ik0"
,
DemoUtil
.
TYPE_DASH
_VOD
,
false
,
true
),
+
"C0358CE1E335417D9A8D88FF192F0D5D8F6DA1B6&key=ik0"
,
DemoUtil
.
TYPE_DASH
,
false
,
true
),
};
};
public
static
final
Sample
[]
SMOOTHSTREAMING
=
new
Sample
[]
{
public
static
final
Sample
[]
SMOOTHSTREAMING
=
new
Sample
[]
{
...
@@ -106,32 +106,32 @@ package com.google.android.exoplayer.demo;
...
@@ -106,32 +106,32 @@ package com.google.android.exoplayer.demo;
"http://www.youtube.com/api/manifest/dash/id/d286538032258a1c/source/youtube?"
"http://www.youtube.com/api/manifest/dash/id/d286538032258a1c/source/youtube?"
+
"as=fmp4_audio_cenc,fmp4_sd_hd_cenc&sparams=ip,ipbits,expire,as&ip=0.0.0.0&ipbits=0"
+
"as=fmp4_audio_cenc,fmp4_sd_hd_cenc&sparams=ip,ipbits,expire,as&ip=0.0.0.0&ipbits=0"
+
"&expire=19000000000&signature=41EA40A027A125A16292E0A5E3277A3B5FA9B938."
+
"&expire=19000000000&signature=41EA40A027A125A16292E0A5E3277A3B5FA9B938."
+
"0BB075C396FFDDC97E526E8F77DC26FF9667D0D6&key=ik0"
,
DemoUtil
.
TYPE_DASH
_VOD
,
true
,
true
),
+
"0BB075C396FFDDC97E526E8F77DC26FF9667D0D6&key=ik0"
,
DemoUtil
.
TYPE_DASH
,
true
,
true
),
new
Sample
(
"WV: HDCP not required"
,
"48fcc369939ac96c"
,
new
Sample
(
"WV: HDCP not required"
,
"48fcc369939ac96c"
,
"http://www.youtube.com/api/manifest/dash/id/48fcc369939ac96c/source/youtube?"
"http://www.youtube.com/api/manifest/dash/id/48fcc369939ac96c/source/youtube?"
+
"as=fmp4_audio_cenc,fmp4_sd_hd_cenc&sparams=ip,ipbits,expire,as&ip=0.0.0.0&ipbits=0"
+
"as=fmp4_audio_cenc,fmp4_sd_hd_cenc&sparams=ip,ipbits,expire,as&ip=0.0.0.0&ipbits=0"
+
"&expire=19000000000&signature=315911BDCEED0FB0C763455BDCC97449DAAFA9E8."
+
"&expire=19000000000&signature=315911BDCEED0FB0C763455BDCC97449DAAFA9E8."
+
"5B41E2EB411F797097A359D6671D2CDE26272373&key=ik0"
,
DemoUtil
.
TYPE_DASH
_VOD
,
true
,
true
),
+
"5B41E2EB411F797097A359D6671D2CDE26272373&key=ik0"
,
DemoUtil
.
TYPE_DASH
,
true
,
true
),
new
Sample
(
"WV: HDCP required"
,
"e06c39f1151da3df"
,
new
Sample
(
"WV: HDCP required"
,
"e06c39f1151da3df"
,
"http://www.youtube.com/api/manifest/dash/id/e06c39f1151da3df/source/youtube?"
"http://www.youtube.com/api/manifest/dash/id/e06c39f1151da3df/source/youtube?"
+
"as=fmp4_audio_cenc,fmp4_sd_hd_cenc&sparams=ip,ipbits,expire,as&ip=0.0.0.0&ipbits=0"
+
"as=fmp4_audio_cenc,fmp4_sd_hd_cenc&sparams=ip,ipbits,expire,as&ip=0.0.0.0&ipbits=0"
+
"&expire=19000000000&signature=A47A1E13E7243BD567601A75F79B34644D0DC592."
+
"&expire=19000000000&signature=A47A1E13E7243BD567601A75F79B34644D0DC592."
+
"B09589A34FA23527EFC1552907754BB8033870BD&key=ik0"
,
DemoUtil
.
TYPE_DASH
_VOD
,
true
,
true
),
+
"B09589A34FA23527EFC1552907754BB8033870BD&key=ik0"
,
DemoUtil
.
TYPE_DASH
,
true
,
true
),
new
Sample
(
"WV: Secure video path required"
,
"0894c7c8719b28a0"
,
new
Sample
(
"WV: Secure video path required"
,
"0894c7c8719b28a0"
,
"http://www.youtube.com/api/manifest/dash/id/0894c7c8719b28a0/source/youtube?"
"http://www.youtube.com/api/manifest/dash/id/0894c7c8719b28a0/source/youtube?"
+
"as=fmp4_audio_cenc,fmp4_sd_hd_cenc&sparams=ip,ipbits,expire,as&ip=0.0.0.0&ipbits=0"
+
"as=fmp4_audio_cenc,fmp4_sd_hd_cenc&sparams=ip,ipbits,expire,as&ip=0.0.0.0&ipbits=0"
+
"&expire=19000000000&signature=2847EE498970F6B45176766CD2802FEB4D4CB7B2."
+
"&expire=19000000000&signature=2847EE498970F6B45176766CD2802FEB4D4CB7B2."
+
"A1CA51EC40A1C1039BA800C41500DD448C03EEDA&key=ik0"
,
DemoUtil
.
TYPE_DASH
_VOD
,
true
,
true
),
+
"A1CA51EC40A1C1039BA800C41500DD448C03EEDA&key=ik0"
,
DemoUtil
.
TYPE_DASH
,
true
,
true
),
new
Sample
(
"WV: HDCP + secure video path required"
,
"efd045b1eb61888a"
,
new
Sample
(
"WV: HDCP + secure video path required"
,
"efd045b1eb61888a"
,
"http://www.youtube.com/api/manifest/dash/id/efd045b1eb61888a/source/youtube?"
"http://www.youtube.com/api/manifest/dash/id/efd045b1eb61888a/source/youtube?"
+
"as=fmp4_audio_cenc,fmp4_sd_hd_cenc&sparams=ip,ipbits,expire,as&ip=0.0.0.0&ipbits=0"
+
"as=fmp4_audio_cenc,fmp4_sd_hd_cenc&sparams=ip,ipbits,expire,as&ip=0.0.0.0&ipbits=0"
+
"&expire=19000000000&signature=61611F115EEEC7BADE5536827343FFFE2D83D14F."
+
"&expire=19000000000&signature=61611F115EEEC7BADE5536827343FFFE2D83D14F."
+
"2FDF4BFA502FB5865C5C86401314BDDEA4799BD0&key=ik0"
,
DemoUtil
.
TYPE_DASH
_VOD
,
true
,
true
),
+
"2FDF4BFA502FB5865C5C86401314BDDEA4799BD0&key=ik0"
,
DemoUtil
.
TYPE_DASH
,
true
,
true
),
new
Sample
(
"WV: 30s license duration"
,
"f9a34cab7b05881a"
,
new
Sample
(
"WV: 30s license duration"
,
"f9a34cab7b05881a"
,
"http://www.youtube.com/api/manifest/dash/id/f9a34cab7b05881a/source/youtube?"
"http://www.youtube.com/api/manifest/dash/id/f9a34cab7b05881a/source/youtube?"
+
"as=fmp4_audio_cenc,fmp4_sd_hd_cenc&sparams=ip,ipbits,expire,as&ip=0.0.0.0&ipbits=0"
+
"as=fmp4_audio_cenc,fmp4_sd_hd_cenc&sparams=ip,ipbits,expire,as&ip=0.0.0.0&ipbits=0"
+
"&expire=19000000000&signature=88DC53943385CED8CF9F37ADD9E9843E3BF621E6."
+
"&expire=19000000000&signature=88DC53943385CED8CF9F37ADD9E9843E3BF621E6."
+
"22727BB612D24AA4FACE4EF62726F9461A9BF57A&key=ik0"
,
DemoUtil
.
TYPE_DASH
_VOD
,
true
,
true
),
+
"22727BB612D24AA4FACE4EF62726F9461A9BF57A&key=ik0"
,
DemoUtil
.
TYPE_DASH
,
true
,
true
),
};
};
public
static
final
Sample
[]
HLS
=
new
Sample
[]
{
public
static
final
Sample
[]
HLS
=
new
Sample
[]
{
...
...
demo/src/main/java/com/google/android/exoplayer/demo/full/FullPlayerActivity.java
View file @
511dd943
...
@@ -19,7 +19,7 @@ import com.google.android.exoplayer.ExoPlayer;
...
@@ -19,7 +19,7 @@ import com.google.android.exoplayer.ExoPlayer;
import
com.google.android.exoplayer.VideoSurfaceView
;
import
com.google.android.exoplayer.VideoSurfaceView
;
import
com.google.android.exoplayer.demo.DemoUtil
;
import
com.google.android.exoplayer.demo.DemoUtil
;
import
com.google.android.exoplayer.demo.R
;
import
com.google.android.exoplayer.demo.R
;
import
com.google.android.exoplayer.demo.full.player.Dash
Vod
RendererBuilder
;
import
com.google.android.exoplayer.demo.full.player.DashRendererBuilder
;
import
com.google.android.exoplayer.demo.full.player.DefaultRendererBuilder
;
import
com.google.android.exoplayer.demo.full.player.DefaultRendererBuilder
;
import
com.google.android.exoplayer.demo.full.player.DemoPlayer
;
import
com.google.android.exoplayer.demo.full.player.DemoPlayer
;
import
com.google.android.exoplayer.demo.full.player.DemoPlayer.RendererBuilder
;
import
com.google.android.exoplayer.demo.full.player.DemoPlayer.RendererBuilder
;
...
@@ -179,8 +179,8 @@ public class FullPlayerActivity extends Activity implements SurfaceHolder.Callba
...
@@ -179,8 +179,8 @@ public class FullPlayerActivity extends Activity implements SurfaceHolder.Callba
case
DemoUtil
.
TYPE_SS
:
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
:
return
new
Dash
Vod
RendererBuilder
(
userAgent
,
contentUri
.
toString
(),
contentId
,
return
new
DashRendererBuilder
(
userAgent
,
contentUri
.
toString
(),
contentId
,
new
WidevineTestMediaDrmCallback
(
contentId
),
debugTextView
);
new
WidevineTestMediaDrmCallback
(
contentId
),
debugTextView
);
case
DemoUtil
.
TYPE_HLS
:
case
DemoUtil
.
TYPE_HLS
:
return
new
HlsRendererBuilder
(
userAgent
,
contentUri
.
toString
(),
contentId
);
return
new
HlsRendererBuilder
(
userAgent
,
contentUri
.
toString
(),
contentId
);
...
...
demo/src/main/java/com/google/android/exoplayer/demo/full/player/Dash
Vod
RendererBuilder.java
→
demo/src/main/java/com/google/android/exoplayer/demo/full/player/DashRendererBuilder.java
View file @
511dd943
...
@@ -40,6 +40,8 @@ import com.google.android.exoplayer.demo.full.player.DemoPlayer.RendererBuilderC
...
@@ -40,6 +40,8 @@ import com.google.android.exoplayer.demo.full.player.DemoPlayer.RendererBuilderC
import
com.google.android.exoplayer.drm.DrmSessionManager
;
import
com.google.android.exoplayer.drm.DrmSessionManager
;
import
com.google.android.exoplayer.drm.MediaDrmCallback
;
import
com.google.android.exoplayer.drm.MediaDrmCallback
;
import
com.google.android.exoplayer.drm.StreamingDrmSessionManager
;
import
com.google.android.exoplayer.drm.StreamingDrmSessionManager
;
import
com.google.android.exoplayer.text.TextTrackRenderer
;
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
;
...
@@ -58,16 +60,19 @@ import android.widget.TextView;
...
@@ -58,16 +60,19 @@ import android.widget.TextView;
import
java.io.IOException
;
import
java.io.IOException
;
import
java.util.ArrayList
;
import
java.util.ArrayList
;
import
java.util.List
;
/**
/**
* A {@link RendererBuilder} for DASH
VOD
.
* A {@link RendererBuilder} for DASH.
*/
*/
public
class
Dash
Vod
RendererBuilder
implements
RendererBuilder
,
public
class
DashRendererBuilder
implements
RendererBuilder
,
ManifestCallback
<
MediaPresentationDescription
>
{
ManifestCallback
<
MediaPresentationDescription
>
{
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
TEXT_BUFFER_SEGMENTS
=
2
;
private
static
final
int
LIVE_EDGE_LATENCY_MS
=
30000
;
private
static
final
int
SECURITY_LEVEL_UNKNOWN
=
-
1
;
private
static
final
int
SECURITY_LEVEL_UNKNOWN
=
-
1
;
private
static
final
int
SECURITY_LEVEL_1
=
1
;
private
static
final
int
SECURITY_LEVEL_1
=
1
;
...
@@ -81,8 +86,9 @@ public class DashVodRendererBuilder implements RendererBuilder,
...
@@ -81,8 +86,9 @@ public class DashVodRendererBuilder implements RendererBuilder,
private
DemoPlayer
player
;
private
DemoPlayer
player
;
private
RendererBuilderCallback
callback
;
private
RendererBuilderCallback
callback
;
private
ManifestFetcher
<
MediaPresentationDescription
>
manifestFetcher
;
public
Dash
Vod
RendererBuilder
(
String
userAgent
,
String
url
,
String
contentId
,
public
DashRendererBuilder
(
String
userAgent
,
String
url
,
String
contentId
,
MediaDrmCallback
drmCallback
,
TextView
debugTextView
)
{
MediaDrmCallback
drmCallback
,
TextView
debugTextView
)
{
this
.
userAgent
=
userAgent
;
this
.
userAgent
=
userAgent
;
this
.
url
=
url
;
this
.
url
=
url
;
...
@@ -96,8 +102,8 @@ public class DashVodRendererBuilder implements RendererBuilder,
...
@@ -96,8 +102,8 @@ public class DashVodRendererBuilder implements RendererBuilder,
this
.
player
=
player
;
this
.
player
=
player
;
this
.
callback
=
callback
;
this
.
callback
=
callback
;
MediaPresentationDescriptionParser
parser
=
new
MediaPresentationDescriptionParser
();
MediaPresentationDescriptionParser
parser
=
new
MediaPresentationDescriptionParser
();
ManifestFetcher
<
MediaPresentationDescription
>
manifestFetcher
=
manifestFetcher
=
new
ManifestFetcher
<
MediaPresentationDescription
>(
parser
,
contentId
,
url
,
new
ManifestFetcher
<
MediaPresentationDescription
>(
parser
,
contentId
,
url
,
userAgent
);
userAgent
);
manifestFetcher
.
singleLoad
(
player
.
getMainHandler
().
getLooper
(),
this
);
manifestFetcher
.
singleLoad
(
player
.
getMainHandler
().
getLooper
(),
this
);
}
}
...
@@ -108,38 +114,17 @@ public class DashVodRendererBuilder implements RendererBuilder,
...
@@ -108,38 +114,17 @@ public class DashVodRendererBuilder implements RendererBuilder,
@Override
@Override
public
void
onManifest
(
String
contentId
,
MediaPresentationDescription
manifest
)
{
public
void
onManifest
(
String
contentId
,
MediaPresentationDescription
manifest
)
{
Period
period
=
manifest
.
periods
.
get
(
0
);
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
);
// Obtain Representations for playback.
int
videoAdaptationSetIndex
=
period
.
getAdaptationSetIndex
(
AdaptationSet
.
TYPE_VIDEO
);
int
maxDecodableFrameSize
=
MediaCodecUtil
.
maxH264DecodableFrameSize
();
AdaptationSet
videoAdaptationSet
=
period
.
adaptationSets
.
get
(
videoAdaptationSetIndex
);
ArrayList
<
Representation
>
audioRepresentationsList
=
new
ArrayList
<
Representation
>();
ArrayList
<
Representation
>
videoRepresentationsList
=
new
ArrayList
<
Representation
>();
Period
period
=
manifest
.
periods
.
get
(
0
);
boolean
hasContentProtection
=
false
;
for
(
int
i
=
0
;
i
<
period
.
adaptationSets
.
size
();
i
++)
{
AdaptationSet
adaptationSet
=
period
.
adaptationSets
.
get
(
i
);
hasContentProtection
|=
adaptationSet
.
hasContentProtection
();
int
adaptationSetType
=
adaptationSet
.
type
;
for
(
int
j
=
0
;
j
<
adaptationSet
.
representations
.
size
();
j
++)
{
Representation
representation
=
adaptationSet
.
representations
.
get
(
j
);
if
(
adaptationSetType
==
AdaptationSet
.
TYPE_AUDIO
)
{
audioRepresentationsList
.
add
(
representation
);
}
else
if
(
adaptationSetType
==
AdaptationSet
.
TYPE_VIDEO
)
{
Format
format
=
representation
.
format
;
if
(
format
.
width
*
format
.
height
<=
maxDecodableFrameSize
)
{
videoRepresentationsList
.
add
(
representation
);
}
else
{
// The device isn't capable of playing this stream.
}
}
}
}
Representation
[]
videoRepresentations
=
new
Representation
[
videoRepresentationsList
.
size
()];
videoRepresentationsList
.
toArray
(
videoRepresentations
);
// Check drm support if necessary.
// Check drm support if necessary.
boolean
hasContentProtection
=
videoAdaptationSet
.
hasContentProtection
();
boolean
filterHdContent
=
false
;
DrmSessionManager
drmSessionManager
=
null
;
DrmSessionManager
drmSessionManager
=
null
;
if
(
hasContentProtection
)
{
if
(
hasContentProtection
)
{
if
(
Util
.
SDK_INT
<
18
)
{
if
(
Util
.
SDK_INT
<
18
)
{
...
@@ -151,55 +136,81 @@ public class DashVodRendererBuilder implements RendererBuilder,
...
@@ -151,55 +136,81 @@ public class DashVodRendererBuilder implements RendererBuilder,
Pair
<
DrmSessionManager
,
Boolean
>
drmSessionManagerData
=
Pair
<
DrmSessionManager
,
Boolean
>
drmSessionManagerData
=
V18Compat
.
getDrmSessionManagerData
(
player
,
drmCallback
);
V18Compat
.
getDrmSessionManagerData
(
player
,
drmCallback
);
drmSessionManager
=
drmSessionManagerData
.
first
;
drmSessionManager
=
drmSessionManagerData
.
first
;
if
(!
drmSessionManagerData
.
second
)
{
// HD streams require L1 security.
// HD streams require L1 security.
videoRepresentations
=
getSdRepresentations
(
videoRepresentations
);
filterHdContent
=
!
drmSessionManagerData
.
second
;
}
}
catch
(
Exception
e
)
{
}
catch
(
Exception
e
)
{
callback
.
onRenderersError
(
e
);
callback
.
onRenderersError
(
e
);
return
;
return
;
}
}
}
}
// Build the video renderer.
// Determine which video representations we should use for playback.
DataSource
videoDataSource
=
new
UriDataSource
(
userAgent
,
bandwidthMeter
);
int
maxDecodableFrameSize
=
MediaCodecUtil
.
maxH264DecodableFrameSize
();
ChunkSource
videoChunkSource
;
List
<
Representation
>
videoRepresentations
=
videoAdaptationSet
.
representations
;
String
mimeType
=
videoRepresentations
[
0
].
format
.
mimeType
;
ArrayList
<
Integer
>
videoRepresentationIndexList
=
new
ArrayList
<
Integer
>();
if
(
mimeType
.
equals
(
MimeTypes
.
VIDEO_MP4
)
||
mimeType
.
equals
(
MimeTypes
.
VIDEO_WEBM
))
{
for
(
int
i
=
0
;
i
<
videoRepresentations
.
size
();
i
++)
{
videoChunkSource
=
new
DashChunkSource
(
videoDataSource
,
Format
format
=
videoRepresentations
.
get
(
i
).
format
;
new
AdaptiveEvaluator
(
bandwidthMeter
),
videoRepresentations
);
if
(
filterHdContent
&&
(
format
.
width
>=
1280
||
format
.
height
>=
720
))
{
// Filtering HD content
}
else
if
(
format
.
width
*
format
.
height
>
maxDecodableFrameSize
)
{
// Filtering stream that device cannot play
}
else
if
(!
format
.
mimeType
.
equals
(
MimeTypes
.
VIDEO_MP4
)
&&
!
format
.
mimeType
.
equals
(
MimeTypes
.
VIDEO_WEBM
))
{
// Filtering unsupported mime type
}
else
{
}
else
{
throw
new
IllegalStateException
(
"Unexpected mime type: "
+
mimeType
);
videoRepresentationIndexList
.
add
(
i
);
}
}
}
// Build the video renderer.
final
MediaCodecVideoTrackRenderer
videoRenderer
;
final
TrackRenderer
debugRenderer
;
if
(
videoRepresentationIndexList
.
isEmpty
())
{
videoRenderer
=
null
;
debugRenderer
=
null
;
}
else
{
int
[]
videoRepresentationIndices
=
Util
.
toArray
(
videoRepresentationIndexList
);
DataSource
videoDataSource
=
new
UriDataSource
(
userAgent
,
bandwidthMeter
);
ChunkSource
videoChunkSource
=
new
DashChunkSource
(
manifestFetcher
,
videoAdaptationSetIndex
,
videoRepresentationIndices
,
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
,
mainHandler
,
player
,
VIDEO_BUFFER_SEGMENTS
*
BUFFER_SEGMENT_SIZE
,
true
,
mainHandler
,
player
,
DemoPlayer
.
TYPE_VIDEO
);
DemoPlayer
.
TYPE_VIDEO
);
MediaCodecVideoTrackRenderer
videoRenderer
=
new
MediaCodecVideoTrackRenderer
(
videoSampleSource
,
videoRenderer
=
new
MediaCodecVideoTrackRenderer
(
videoSampleSource
,
drmSessionManager
,
true
,
drmSessionManager
,
true
,
MediaCodec
.
VIDEO_SCALING_MODE_SCALE_TO_FIT
,
5000
,
null
,
MediaCodec
.
VIDEO_SCALING_MODE_SCALE_TO_FIT
,
5000
,
null
,
mainHandler
,
player
,
50
);
mainHandler
,
player
,
50
);
debugRenderer
=
debugTextView
!=
null
?
new
DebugTrackRenderer
(
debugTextView
,
videoRenderer
,
videoSampleSource
)
:
null
;
}
// Build the audio chunk sources.
int
audioAdaptationSetIndex
=
period
.
getAdaptationSetIndex
(
AdaptationSet
.
TYPE_AUDIO
);
AdaptationSet
audioAdaptationSet
=
period
.
adaptationSets
.
get
(
audioAdaptationSetIndex
);
DataSource
audioDataSource
=
new
UriDataSource
(
userAgent
,
bandwidthMeter
);
FormatEvaluator
audioEvaluator
=
new
FormatEvaluator
.
FixedEvaluator
();
List
<
ChunkSource
>
audioChunkSourceList
=
new
ArrayList
<
ChunkSource
>();
List
<
String
>
audioTrackNameList
=
new
ArrayList
<
String
>();
List
<
Representation
>
audioRepresentations
=
audioAdaptationSet
.
representations
;
for
(
int
i
=
0
;
i
<
audioRepresentations
.
size
();
i
++)
{
Format
format
=
audioRepresentations
.
get
(
i
).
format
;
audioTrackNameList
.
add
(
format
.
id
+
" ("
+
format
.
numChannels
+
"ch, "
+
format
.
audioSamplingRate
+
"Hz)"
);
audioChunkSourceList
.
add
(
new
DashChunkSource
(
manifestFetcher
,
audioAdaptationSetIndex
,
new
int
[]
{
i
},
audioDataSource
,
audioEvaluator
,
LIVE_EDGE_LATENCY_MS
));
}
// Build the audio renderer.
// Build the audio renderer.
final
String
[]
audioTrackNames
;
final
String
[]
audioTrackNames
;
final
MultiTrackChunkSource
audioChunkSource
;
final
MultiTrackChunkSource
audioChunkSource
;
final
TrackRenderer
audioRenderer
;
final
TrackRenderer
audioRenderer
;
if
(
audio
Representations
List
.
isEmpty
())
{
if
(
audio
ChunkSource
List
.
isEmpty
())
{
audioTrackNames
=
null
;
audioTrackNames
=
null
;
audioChunkSource
=
null
;
audioChunkSource
=
null
;
audioRenderer
=
null
;
audioRenderer
=
null
;
}
else
{
}
else
{
DataSource
audioDataSource
=
new
UriDataSource
(
userAgent
,
bandwidthMeter
);
audioTrackNames
=
new
String
[
audioTrackNameList
.
size
()];
audioTrackNames
=
new
String
[
audioRepresentationsList
.
size
()];
audioTrackNameList
.
toArray
(
audioTrackNames
);
ChunkSource
[]
audioChunkSources
=
new
ChunkSource
[
audioRepresentationsList
.
size
()];
audioChunkSource
=
new
MultiTrackChunkSource
(
audioChunkSourceList
);
FormatEvaluator
audioEvaluator
=
new
FormatEvaluator
.
FixedEvaluator
();
for
(
int
i
=
0
;
i
<
audioRepresentationsList
.
size
();
i
++)
{
Representation
representation
=
audioRepresentationsList
.
get
(
i
);
Format
format
=
representation
.
format
;
audioTrackNames
[
i
]
=
format
.
id
+
" ("
+
format
.
numChannels
+
"ch, "
+
format
.
audioSamplingRate
+
"Hz)"
;
audioChunkSources
[
i
]
=
new
DashChunkSource
(
audioDataSource
,
audioEvaluator
,
representation
);
}
audioChunkSource
=
new
MultiTrackChunkSource
(
audioChunkSources
);
SampleSource
audioSampleSource
=
new
ChunkSampleSource
(
audioChunkSource
,
loadControl
,
SampleSource
audioSampleSource
=
new
ChunkSampleSource
(
audioChunkSource
,
loadControl
,
AUDIO_BUFFER_SEGMENTS
*
BUFFER_SEGMENT_SIZE
,
true
,
mainHandler
,
player
,
AUDIO_BUFFER_SEGMENTS
*
BUFFER_SEGMENT_SIZE
,
true
,
mainHandler
,
player
,
DemoPlayer
.
TYPE_AUDIO
);
DemoPlayer
.
TYPE_AUDIO
);
...
@@ -207,37 +218,61 @@ public class DashVodRendererBuilder implements RendererBuilder,
...
@@ -207,37 +218,61 @@ public class DashVodRendererBuilder implements RendererBuilder,
mainHandler
,
player
);
mainHandler
,
player
);
}
}
// Build the debug renderer.
// Build the text chunk sources.
TrackRenderer
debugRenderer
=
debugTextView
!=
null
DataSource
textDataSource
=
new
UriDataSource
(
userAgent
,
bandwidthMeter
);
?
new
DebugTrackRenderer
(
debugTextView
,
videoRenderer
,
videoSampleSource
)
:
null
;
FormatEvaluator
textEvaluator
=
new
FormatEvaluator
.
FixedEvaluator
();
List
<
ChunkSource
>
textChunkSourceList
=
new
ArrayList
<
ChunkSource
>();
List
<
String
>
textTrackNameList
=
new
ArrayList
<
String
>();
for
(
int
i
=
0
;
i
<
period
.
adaptationSets
.
size
();
i
++)
{
AdaptationSet
adaptationSet
=
period
.
adaptationSets
.
get
(
i
);
if
(
adaptationSet
.
type
==
AdaptationSet
.
TYPE_TEXT
)
{
List
<
Representation
>
representations
=
adaptationSet
.
representations
;
for
(
int
j
=
0
;
j
<
representations
.
size
();
j
++)
{
Representation
representation
=
representations
.
get
(
j
);
textTrackNameList
.
add
(
representation
.
format
.
id
);
textChunkSourceList
.
add
(
new
DashChunkSource
(
manifestFetcher
,
i
,
new
int
[]
{
j
},
textDataSource
,
textEvaluator
,
LIVE_EDGE_LATENCY_MS
));
}
}
}
// Build the text renderers
final
String
[]
textTrackNames
;
final
MultiTrackChunkSource
textChunkSource
;
final
TrackRenderer
textRenderer
;
if
(
textChunkSourceList
.
isEmpty
())
{
textTrackNames
=
null
;
textChunkSource
=
null
;
textRenderer
=
null
;
}
else
{
textTrackNames
=
new
String
[
textTrackNameList
.
size
()];
textTrackNameList
.
toArray
(
textTrackNames
);
textChunkSource
=
new
MultiTrackChunkSource
(
textChunkSourceList
);
SampleSource
textSampleSource
=
new
ChunkSampleSource
(
textChunkSource
,
loadControl
,
TEXT_BUFFER_SEGMENTS
*
BUFFER_SEGMENT_SIZE
,
true
,
mainHandler
,
player
,
DemoPlayer
.
TYPE_TEXT
);
textRenderer
=
new
TextTrackRenderer
(
textSampleSource
,
new
WebvttParser
(),
player
,
mainHandler
.
getLooper
());
}
// Invoke the callback.
// Invoke the callback.
String
[][]
trackNames
=
new
String
[
DemoPlayer
.
RENDERER_COUNT
][];
String
[][]
trackNames
=
new
String
[
DemoPlayer
.
RENDERER_COUNT
][];
trackNames
[
DemoPlayer
.
TYPE_AUDIO
]
=
audioTrackNames
;
trackNames
[
DemoPlayer
.
TYPE_AUDIO
]
=
audioTrackNames
;
trackNames
[
DemoPlayer
.
TYPE_TEXT
]
=
textTrackNames
;
MultiTrackChunkSource
[]
multiTrackChunkSources
=
MultiTrackChunkSource
[]
multiTrackChunkSources
=
new
MultiTrackChunkSource
[
DemoPlayer
.
RENDERER_COUNT
];
new
MultiTrackChunkSource
[
DemoPlayer
.
RENDERER_COUNT
];
multiTrackChunkSources
[
DemoPlayer
.
TYPE_AUDIO
]
=
audioChunkSource
;
multiTrackChunkSources
[
DemoPlayer
.
TYPE_AUDIO
]
=
audioChunkSource
;
multiTrackChunkSources
[
DemoPlayer
.
TYPE_TEXT
]
=
textChunkSource
;
TrackRenderer
[]
renderers
=
new
TrackRenderer
[
DemoPlayer
.
RENDERER_COUNT
];
TrackRenderer
[]
renderers
=
new
TrackRenderer
[
DemoPlayer
.
RENDERER_COUNT
];
renderers
[
DemoPlayer
.
TYPE_VIDEO
]
=
videoRenderer
;
renderers
[
DemoPlayer
.
TYPE_VIDEO
]
=
videoRenderer
;
renderers
[
DemoPlayer
.
TYPE_AUDIO
]
=
audioRenderer
;
renderers
[
DemoPlayer
.
TYPE_AUDIO
]
=
audioRenderer
;
renderers
[
DemoPlayer
.
TYPE_TEXT
]
=
textRenderer
;
renderers
[
DemoPlayer
.
TYPE_DEBUG
]
=
debugRenderer
;
renderers
[
DemoPlayer
.
TYPE_DEBUG
]
=
debugRenderer
;
callback
.
onRenderers
(
trackNames
,
multiTrackChunkSources
,
renderers
);
callback
.
onRenderers
(
trackNames
,
multiTrackChunkSources
,
renderers
);
}
}
private
Representation
[]
getSdRepresentations
(
Representation
[]
representations
)
{
ArrayList
<
Representation
>
sdRepresentations
=
new
ArrayList
<
Representation
>();
for
(
int
i
=
0
;
i
<
representations
.
length
;
i
++)
{
if
(
representations
[
i
].
format
.
height
<
720
&&
representations
[
i
].
format
.
width
<
1280
)
{
sdRepresentations
.
add
(
representations
[
i
]);
}
}
Representation
[]
sdRepresentationArray
=
new
Representation
[
sdRepresentations
.
size
()];
sdRepresentations
.
toArray
(
sdRepresentationArray
);
return
sdRepresentationArray
;
}
@TargetApi
(
18
)
@TargetApi
(
18
)
private
static
class
V18Compat
{
private
static
class
V18Compat
{
...
...
demo/src/main/java/com/google/android/exoplayer/demo/full/player/SmoothStreamingRendererBuilder.java
View file @
511dd943
...
@@ -64,7 +64,7 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
...
@@ -64,7 +64,7 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
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
T
TML
_BUFFER_SEGMENTS
=
2
;
private
static
final
int
T
EXT
_BUFFER_SEGMENTS
=
2
;
private
static
final
int
LIVE_EDGE_LATENCY_MS
=
30000
;
private
static
final
int
LIVE_EDGE_LATENCY_MS
=
30000
;
private
final
String
userAgent
;
private
final
String
userAgent
;
...
@@ -149,10 +149,7 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
...
@@ -149,10 +149,7 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
}
}
}
}
}
}
int
[]
videoTrackIndices
=
new
int
[
videoTrackIndexList
.
size
()];
int
[]
videoTrackIndices
=
Util
.
toArray
(
videoTrackIndexList
);
for
(
int
i
=
0
;
i
<
videoTrackIndexList
.
size
();
i
++)
{
videoTrackIndices
[
i
]
=
videoTrackIndexList
.
get
(
i
);
}
// Build the video renderer.
// Build the video renderer.
DataSource
videoDataSource
=
new
UriDataSource
(
userAgent
,
bandwidthMeter
);
DataSource
videoDataSource
=
new
UriDataSource
(
userAgent
,
bandwidthMeter
);
...
@@ -221,7 +218,7 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
...
@@ -221,7 +218,7 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
}
}
textChunkSource
=
new
MultiTrackChunkSource
(
textChunkSources
);
textChunkSource
=
new
MultiTrackChunkSource
(
textChunkSources
);
ChunkSampleSource
ttmlSampleSource
=
new
ChunkSampleSource
(
textChunkSource
,
loadControl
,
ChunkSampleSource
ttmlSampleSource
=
new
ChunkSampleSource
(
textChunkSource
,
loadControl
,
T
TML
_BUFFER_SEGMENTS
*
BUFFER_SEGMENT_SIZE
,
true
,
mainHandler
,
player
,
T
EXT
_BUFFER_SEGMENTS
*
BUFFER_SEGMENT_SIZE
,
true
,
mainHandler
,
player
,
DemoPlayer
.
TYPE_TEXT
);
DemoPlayer
.
TYPE_TEXT
);
textRenderer
=
new
TextTrackRenderer
(
ttmlSampleSource
,
new
TtmlParser
(),
player
,
textRenderer
=
new
TextTrackRenderer
(
ttmlSampleSource
,
new
TtmlParser
(),
player
,
mainHandler
.
getLooper
());
mainHandler
.
getLooper
());
...
...
demo/src/main/java/com/google/android/exoplayer/demo/simple/Dash
Vod
RendererBuilder.java
→
demo/src/main/java/com/google/android/exoplayer/demo/simple/DashRendererBuilder.java
View file @
511dd943
...
@@ -40,22 +40,26 @@ import com.google.android.exoplayer.upstream.DefaultBandwidthMeter;
...
@@ -40,22 +40,26 @@ import com.google.android.exoplayer.upstream.DefaultBandwidthMeter;
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
;
import
com.google.android.exoplayer.util.MimeTypes
;
import
com.google.android.exoplayer.util.Util
;
import
android.media.MediaCodec
;
import
android.media.MediaCodec
;
import
android.os.Handler
;
import
android.os.Handler
;
import
java.io.IOException
;
import
java.io.IOException
;
import
java.util.ArrayList
;
import
java.util.ArrayList
;
import
java.util.List
;
/**
/**
* A {@link RendererBuilder} for DASH
VOD
.
* A {@link RendererBuilder} for DASH.
*/
*/
/* package */
class
Dash
Vod
RendererBuilder
implements
RendererBuilder
,
/* package */
class
DashRendererBuilder
implements
RendererBuilder
,
ManifestCallback
<
MediaPresentationDescription
>
{
ManifestCallback
<
MediaPresentationDescription
>
{
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
;
...
@@ -63,8 +67,9 @@ import java.util.ArrayList;
...
@@ -63,8 +67,9 @@ import java.util.ArrayList;
private
final
String
contentId
;
private
final
String
contentId
;
private
RendererBuilderCallback
callback
;
private
RendererBuilderCallback
callback
;
private
ManifestFetcher
<
MediaPresentationDescription
>
manifestFetcher
;
public
Dash
Vod
RendererBuilder
(
SimplePlayerActivity
playerActivity
,
String
userAgent
,
String
url
,
public
DashRendererBuilder
(
SimplePlayerActivity
playerActivity
,
String
userAgent
,
String
url
,
String
contentId
)
{
String
contentId
)
{
this
.
playerActivity
=
playerActivity
;
this
.
playerActivity
=
playerActivity
;
this
.
userAgent
=
userAgent
;
this
.
userAgent
=
userAgent
;
...
@@ -76,8 +81,8 @@ import java.util.ArrayList;
...
@@ -76,8 +81,8 @@ import java.util.ArrayList;
public
void
buildRenderers
(
RendererBuilderCallback
callback
)
{
public
void
buildRenderers
(
RendererBuilderCallback
callback
)
{
this
.
callback
=
callback
;
this
.
callback
=
callback
;
MediaPresentationDescriptionParser
parser
=
new
MediaPresentationDescriptionParser
();
MediaPresentationDescriptionParser
parser
=
new
MediaPresentationDescriptionParser
();
ManifestFetcher
<
MediaPresentationDescription
>
manifestFetcher
=
manifestFetcher
=
new
ManifestFetcher
<
MediaPresentationDescription
>(
parser
,
contentId
,
url
,
new
ManifestFetcher
<
MediaPresentationDescription
>(
parser
,
contentId
,
url
,
userAgent
);
userAgent
);
manifestFetcher
.
singleLoad
(
playerActivity
.
getMainLooper
(),
this
);
manifestFetcher
.
singleLoad
(
playerActivity
.
getMainLooper
(),
this
);
}
}
...
@@ -88,48 +93,50 @@ import java.util.ArrayList;
...
@@ -88,48 +93,50 @@ import java.util.ArrayList;
@Override
@Override
public
void
onManifest
(
String
contentId
,
MediaPresentationDescription
manifest
)
{
public
void
onManifest
(
String
contentId
,
MediaPresentationDescription
manifest
)
{
Period
period
=
manifest
.
periods
.
get
(
0
);
Handler
mainHandler
=
playerActivity
.
getMainHandler
();
Handler
mainHandler
=
playerActivity
.
getMainHandler
();
LoadControl
loadControl
=
new
DefaultLoadControl
(
new
BufferPool
(
BUFFER_SEGMENT_SIZE
));
LoadControl
loadControl
=
new
DefaultLoadControl
(
new
BufferPool
(
BUFFER_SEGMENT_SIZE
));
DefaultBandwidthMeter
bandwidthMeter
=
new
DefaultBandwidthMeter
();
DefaultBandwidthMeter
bandwidthMeter
=
new
DefaultBandwidthMeter
();
//
Obtain Representations
for playback.
//
Determine which video representations we should use
for playback.
int
maxDecodableFrameSize
=
MediaCodecUtil
.
maxH264DecodableFrameSize
();
int
maxDecodableFrameSize
=
MediaCodecUtil
.
maxH264DecodableFrameSize
();
Representation
audioRepresentation
=
null
;
int
videoAdaptationSetIndex
=
period
.
getAdaptationSetIndex
(
AdaptationSet
.
TYPE_VIDEO
);
ArrayList
<
Representation
>
videoRepresentationsList
=
new
ArrayList
<
Representation
>();
List
<
Representation
>
videoRepresentations
=
Period
period
=
manifest
.
periods
.
get
(
0
);
period
.
adaptationSets
.
get
(
videoAdaptationSetIndex
).
representations
;
for
(
int
i
=
0
;
i
<
period
.
adaptationSets
.
size
();
i
++)
{
ArrayList
<
Integer
>
videoRepresentationIndexList
=
new
ArrayList
<
Integer
>();
AdaptationSet
adaptationSet
=
period
.
adaptationSets
.
get
(
i
);
for
(
int
i
=
0
;
i
<
videoRepresentations
.
size
();
i
++)
{
int
adaptationSetType
=
adaptationSet
.
type
;
Format
format
=
videoRepresentations
.
get
(
i
).
format
;
for
(
int
j
=
0
;
j
<
adaptationSet
.
representations
.
size
();
j
++)
{
if
(
format
.
width
*
format
.
height
>
maxDecodableFrameSize
)
{
Representation
representation
=
adaptationSet
.
representations
.
get
(
j
);
// Filtering stream that device cannot play
if
(
audioRepresentation
==
null
&&
adaptationSetType
==
AdaptationSet
.
TYPE_AUDIO
)
{
}
else
if
(!
format
.
mimeType
.
equals
(
MimeTypes
.
VIDEO_MP4
)
audioRepresentation
=
representation
;
&&
!
format
.
mimeType
.
equals
(
MimeTypes
.
VIDEO_WEBM
))
{
}
else
if
(
adaptationSetType
==
AdaptationSet
.
TYPE_VIDEO
)
{
// Filtering unsupported mime type
Format
format
=
representation
.
format
;
if
(
format
.
width
*
format
.
height
<=
maxDecodableFrameSize
)
{
videoRepresentationsList
.
add
(
representation
);
}
else
{
}
else
{
// The device isn't capable of playing this stream.
videoRepresentationIndexList
.
add
(
i
);
}
}
}
}
}
}
Representation
[]
videoRepresentations
=
new
Representation
[
videoRepresentationsList
.
size
()];
videoRepresentationsList
.
toArray
(
videoRepresentations
);
// Build the video renderer.
// Build the video renderer.
final
MediaCodecVideoTrackRenderer
videoRenderer
;
if
(
videoRepresentationIndexList
.
isEmpty
())
{
videoRenderer
=
null
;
}
else
{
int
[]
videoRepresentationIndices
=
Util
.
toArray
(
videoRepresentationIndexList
);
DataSource
videoDataSource
=
new
UriDataSource
(
userAgent
,
bandwidthMeter
);
DataSource
videoDataSource
=
new
UriDataSource
(
userAgent
,
bandwidthMeter
);
ChunkSource
videoChunkSource
=
new
DashChunkSource
(
videoDataSource
,
ChunkSource
videoChunkSource
=
new
DashChunkSource
(
manifestFetcher
,
videoAdaptationSetIndex
,
new
AdaptiveEvaluator
(
bandwidthMeter
),
videoRepresentations
);
videoRepresentationIndices
,
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
,
videoRenderer
=
new
MediaCodecVideoTrackRenderer
(
videoSampleSource
,
MediaCodec
.
VIDEO_SCALING_MODE_SCALE_TO_FIT
,
0
,
mainHandler
,
playerActivity
,
50
);
MediaCodec
.
VIDEO_SCALING_MODE_SCALE_TO_FIT
,
0
,
mainHandler
,
playerActivity
,
50
);
}
// Build the audio renderer.
// Build the audio renderer.
int
audioAdaptationSetIndex
=
period
.
getAdaptationSetIndex
(
AdaptationSet
.
TYPE_AUDIO
);
DataSource
audioDataSource
=
new
UriDataSource
(
userAgent
,
bandwidthMeter
);
DataSource
audioDataSource
=
new
UriDataSource
(
userAgent
,
bandwidthMeter
);
ChunkSource
audioChunkSource
=
new
DashChunkSource
(
audioDataSource
,
ChunkSource
audioChunkSource
=
new
DashChunkSource
(
manifestFetcher
,
audioAdaptationSetIndex
,
new
FormatEvaluator
.
FixedEvaluator
(),
audioRepresentation
);
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
(
...
...
demo/src/main/java/com/google/android/exoplayer/demo/simple/SimplePlayerActivity.java
View file @
511dd943
...
@@ -164,8 +164,8 @@ public class SimplePlayerActivity extends Activity implements SurfaceHolder.Call
...
@@ -164,8 +164,8 @@ public class SimplePlayerActivity extends Activity implements SurfaceHolder.Call
case
DemoUtil
.
TYPE_SS
:
case
DemoUtil
.
TYPE_SS
:
return
new
SmoothStreamingRendererBuilder
(
this
,
userAgent
,
contentUri
.
toString
(),
return
new
SmoothStreamingRendererBuilder
(
this
,
userAgent
,
contentUri
.
toString
(),
contentId
);
contentId
);
case
DemoUtil
.
TYPE_DASH
_VOD
:
case
DemoUtil
.
TYPE_DASH
:
return
new
Dash
Vod
RendererBuilder
(
this
,
userAgent
,
contentUri
.
toString
(),
contentId
);
return
new
DashRendererBuilder
(
this
,
userAgent
,
contentUri
.
toString
(),
contentId
);
case
DemoUtil
.
TYPE_HLS
:
case
DemoUtil
.
TYPE_HLS
:
return
new
HlsRendererBuilder
(
this
,
userAgent
,
contentUri
.
toString
(),
contentId
);
return
new
HlsRendererBuilder
(
this
,
userAgent
,
contentUri
.
toString
(),
contentId
);
default
:
default
:
...
...
demo/src/main/java/com/google/android/exoplayer/demo/simple/SmoothStreamingRendererBuilder.java
View file @
511dd943
...
@@ -38,6 +38,7 @@ import com.google.android.exoplayer.upstream.DefaultBandwidthMeter;
...
@@ -38,6 +38,7 @@ import com.google.android.exoplayer.upstream.DefaultBandwidthMeter;
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
;
import
com.google.android.exoplayer.util.Util
;
import
android.media.MediaCodec
;
import
android.media.MediaCodec
;
import
android.os.Handler
;
import
android.os.Handler
;
...
@@ -115,10 +116,7 @@ import java.util.ArrayList;
...
@@ -115,10 +116,7 @@ import java.util.ArrayList;
}
}
}
}
}
}
int
[]
videoTrackIndices
=
new
int
[
videoTrackIndexList
.
size
()];
int
[]
videoTrackIndices
=
Util
.
toArray
(
videoTrackIndexList
);
for
(
int
i
=
0
;
i
<
videoTrackIndexList
.
size
();
i
++)
{
videoTrackIndices
[
i
]
=
videoTrackIndexList
.
get
(
i
);
}
// Build the video renderer.
// Build the video renderer.
DataSource
videoDataSource
=
new
UriDataSource
(
userAgent
,
bandwidthMeter
);
DataSource
videoDataSource
=
new
UriDataSource
(
userAgent
,
bandwidthMeter
);
...
...
library/src/main/java/com/google/android/exoplayer/C.java
View file @
511dd943
...
@@ -21,6 +21,16 @@ package com.google.android.exoplayer;
...
@@ -21,6 +21,16 @@ package com.google.android.exoplayer;
public
final
class
C
{
public
final
class
C
{
/**
/**
* Represents an unknown microsecond time or duration.
*/
public
static
final
long
UNKNOWN_TIME_US
=
-
1L
;
/**
* The number of microseconds in one second.
*/
public
static
final
long
MICROS_PER_SECOND
=
1000000L
;
/**
* Represents an unbounded length of data.
* Represents an unbounded length of data.
*/
*/
public
static
final
int
LENGTH_UNBOUNDED
=
-
1
;
public
static
final
int
LENGTH_UNBOUNDED
=
-
1
;
...
...
library/src/main/java/com/google/android/exoplayer/FrameworkSampleSource.java
View file @
511dd943
...
@@ -71,10 +71,10 @@ public final class FrameworkSampleSource implements SampleSource {
...
@@ -71,10 +71,10 @@ public final class FrameworkSampleSource implements SampleSource {
trackInfos
=
new
TrackInfo
[
trackStates
.
length
];
trackInfos
=
new
TrackInfo
[
trackStates
.
length
];
for
(
int
i
=
0
;
i
<
trackStates
.
length
;
i
++)
{
for
(
int
i
=
0
;
i
<
trackStates
.
length
;
i
++)
{
android
.
media
.
MediaFormat
format
=
extractor
.
getTrackFormat
(
i
);
android
.
media
.
MediaFormat
format
=
extractor
.
getTrackFormat
(
i
);
long
duration
=
format
.
containsKey
(
android
.
media
.
MediaFormat
.
KEY_DURATION
)
?
long
duration
Us
=
format
.
containsKey
(
android
.
media
.
MediaFormat
.
KEY_DURATION
)
?
format
.
getLong
(
android
.
media
.
MediaFormat
.
KEY_DURATION
)
:
TrackRenderer
.
UNKNOWN_TIME_US
;
format
.
getLong
(
android
.
media
.
MediaFormat
.
KEY_DURATION
)
:
C
.
UNKNOWN_TIME_US
;
String
mime
=
format
.
getString
(
android
.
media
.
MediaFormat
.
KEY_MIME
);
String
mime
=
format
.
getString
(
android
.
media
.
MediaFormat
.
KEY_MIME
);
trackInfos
[
i
]
=
new
TrackInfo
(
mime
,
duration
);
trackInfos
[
i
]
=
new
TrackInfo
(
mime
,
duration
Us
);
}
}
prepared
=
true
;
prepared
=
true
;
}
}
...
...
library/src/main/java/com/google/android/exoplayer/TrackInfo.java
View file @
511dd943
...
@@ -20,9 +20,21 @@ package com.google.android.exoplayer;
...
@@ -20,9 +20,21 @@ package com.google.android.exoplayer;
*/
*/
public
final
class
TrackInfo
{
public
final
class
TrackInfo
{
/**
* The mime type.
*/
public
final
String
mimeType
;
public
final
String
mimeType
;
/**
* The duration in microseconds, or {@link C#UNKNOWN_TIME_US} if the duration is unknown.
*/
public
final
long
durationUs
;
public
final
long
durationUs
;
/**
* @param mimeType The mime type.
* @param durationUs The duration in microseconds, or {@link C#UNKNOWN_TIME_US} if the duration
* is unknown.
*/
public
TrackInfo
(
String
mimeType
,
long
durationUs
)
{
public
TrackInfo
(
String
mimeType
,
long
durationUs
)
{
this
.
mimeType
=
mimeType
;
this
.
mimeType
=
mimeType
;
this
.
durationUs
=
durationUs
;
this
.
durationUs
=
durationUs
;
...
...
library/src/main/java/com/google/android/exoplayer/TrackRenderer.java
View file @
511dd943
...
@@ -67,9 +67,9 @@ public abstract class TrackRenderer implements ExoPlayerComponent {
...
@@ -67,9 +67,9 @@ public abstract class TrackRenderer implements ExoPlayerComponent {
protected
static
final
int
STATE_STARTED
=
3
;
protected
static
final
int
STATE_STARTED
=
3
;
/**
/**
* Represents an unknown time or duration.
* Represents an unknown time or duration.
Equal to {@link C#UNKNOWN_TIME_US}.
*/
*/
public
static
final
long
UNKNOWN_TIME_US
=
-
1
;
public
static
final
long
UNKNOWN_TIME_US
=
C
.
UNKNOWN_TIME_US
;
// -1
/**
/**
* Represents a time or duration that should match the duration of the longest track whose
* Represents a time or duration that should match the duration of the longest track whose
* duration is known.
* duration is known.
...
...
library/src/main/java/com/google/android/exoplayer/audio/AudioCapabilitiesReceiver.java
View file @
511dd943
...
@@ -40,7 +40,7 @@ public final class AudioCapabilitiesReceiver {
...
@@ -40,7 +40,7 @@ public final class AudioCapabilitiesReceiver {
}
}
/** Default to stereo PCM on SDK <
=
21 and when HDMI is unplugged. */
/** Default to stereo PCM on SDK < 21 and when HDMI is unplugged. */
private
static
final
AudioCapabilities
DEFAULT_AUDIO_CAPABILITIES
=
private
static
final
AudioCapabilities
DEFAULT_AUDIO_CAPABILITIES
=
new
AudioCapabilities
(
new
int
[]
{
AudioFormat
.
ENCODING_PCM_16BIT
},
2
);
new
AudioCapabilities
(
new
int
[]
{
AudioFormat
.
ENCODING_PCM_16BIT
},
2
);
...
...
library/src/main/java/com/google/android/exoplayer/audio/AudioTrack.java
View file @
511dd943
...
@@ -15,9 +15,11 @@
...
@@ -15,9 +15,11 @@
*/
*/
package
com
.
google
.
android
.
exoplayer
.
audio
;
package
com
.
google
.
android
.
exoplayer
.
audio
;
import
com.google.android.exoplayer.C
;
import
com.google.android.exoplayer.util.Assertions
;
import
com.google.android.exoplayer.util.Assertions
;
import
com.google.android.exoplayer.util.Util
;
import
com.google.android.exoplayer.util.Util
;
import
android.annotation.SuppressLint
;
import
android.annotation.TargetApi
;
import
android.annotation.TargetApi
;
import
android.media.AudioFormat
;
import
android.media.AudioFormat
;
import
android.media.AudioManager
;
import
android.media.AudioManager
;
...
@@ -80,22 +82,23 @@ public final class AudioTrack {
...
@@ -80,22 +82,23 @@ public final class AudioTrack {
private
static
final
String
TAG
=
"AudioTrack"
;
private
static
final
String
TAG
=
"AudioTrack"
;
private
static
final
long
MICROS_PER_SECOND
=
1000000L
;
/**
/**
* AudioTrack timestamps are deemed spurious if they are offset from the system clock by more
* AudioTrack timestamps are deemed spurious if they are offset from the system clock by more
* than this amount.
* than this amount.
*
*
* <p>This is a fail safe that should not be required on correctly functioning devices.
* <p>This is a fail safe that should not be required on correctly functioning devices.
*/
*/
private
static
final
long
MAX_AUDIO_TIMESTAMP_OFFSET_US
=
10
*
MICROS_PER_SECOND
;
private
static
final
long
MAX_AUDIO_TIMESTAMP_OFFSET_US
=
10
*
C
.
MICROS_PER_SECOND
;
/**
/**
* AudioTrack latencies are deemed impossibly large if they are greater than this amount.
* AudioTrack latencies are deemed impossibly large if they are greater than this amount.
*
*
* <p>This is a fail safe that should not be required on correctly functioning devices.
* <p>This is a fail safe that should not be required on correctly functioning devices.
*/
*/
private
static
final
long
MAX_LATENCY_US
=
10
*
MICROS_PER_SECOND
;
private
static
final
long
MAX_LATENCY_US
=
10
*
C
.
MICROS_PER_SECOND
;
/** Value for ac3Bitrate before the bitrate has been calculated. */
private
static
final
int
UNKNOWN_AC3_BITRATE
=
0
;
private
static
final
int
START_NOT_SET
=
0
;
private
static
final
int
START_NOT_SET
=
0
;
private
static
final
int
START_IN_SYNC
=
1
;
private
static
final
int
START_IN_SYNC
=
1
;
...
@@ -139,6 +142,11 @@ public final class AudioTrack {
...
@@ -139,6 +142,11 @@ public final class AudioTrack {
private
int
temporaryBufferOffset
;
private
int
temporaryBufferOffset
;
private
int
temporaryBufferSize
;
private
int
temporaryBufferSize
;
private
boolean
isAc3
;
/** Bitrate measured in kilobits per second, if {@link #isAc3} is true. */
private
int
ac3Bitrate
;
/** Constructs an audio track using the default minimum buffer size multiplier. */
/** Constructs an audio track using the default minimum buffer size multiplier. */
public
AudioTrack
()
{
public
AudioTrack
()
{
this
(
DEFAULT_MIN_BUFFER_MULTIPLICATION_FACTOR
);
this
(
DEFAULT_MIN_BUFFER_MULTIPLICATION_FACTOR
);
...
@@ -277,6 +285,7 @@ public final class AudioTrack {
...
@@ -277,6 +285,7 @@ public final class AudioTrack {
* @param bufferSize The total size of the playback buffer in bytes. Specify 0 to use a buffer
* @param bufferSize The total size of the playback buffer in bytes. Specify 0 to use a buffer
* size based on the minimum for format.
* size based on the minimum for format.
*/
*/
@SuppressLint
(
"InlinedApi"
)
public
void
reconfigure
(
MediaFormat
format
,
int
encoding
,
int
bufferSize
)
{
public
void
reconfigure
(
MediaFormat
format
,
int
encoding
,
int
bufferSize
)
{
int
channelCount
=
format
.
getInteger
(
MediaFormat
.
KEY_CHANNEL_COUNT
);
int
channelCount
=
format
.
getInteger
(
MediaFormat
.
KEY_CHANNEL_COUNT
);
int
channelConfig
;
int
channelConfig
;
...
@@ -300,8 +309,9 @@ public final class AudioTrack {
...
@@ -300,8 +309,9 @@ public final class AudioTrack {
int
sampleRate
=
format
.
getInteger
(
MediaFormat
.
KEY_SAMPLE_RATE
);
int
sampleRate
=
format
.
getInteger
(
MediaFormat
.
KEY_SAMPLE_RATE
);
// TODO: Does channelConfig determine channelCount?
// TODO: Does channelConfig determine channelCount?
boolean
isAc3
=
encoding
==
AudioFormat
.
ENCODING_AC3
||
encoding
==
AudioFormat
.
ENCODING_E_AC3
;
if
(
audioTrack
!=
null
&&
this
.
sampleRate
==
sampleRate
if
(
audioTrack
!=
null
&&
this
.
sampleRate
==
sampleRate
&&
this
.
channelConfig
==
channelConfig
)
{
&&
this
.
channelConfig
==
channelConfig
&&
!
this
.
isAc3
&&
!
isAc3
)
{
// We already have an existing audio track with the correct sample rate and channel config.
// We already have an existing audio track with the correct sample rate and channel config.
return
;
return
;
}
}
...
@@ -315,7 +325,8 @@ public final class AudioTrack {
...
@@ -315,7 +325,8 @@ public final class AudioTrack {
bufferSize
==
0
?
(
int
)
(
minBufferMultiplicationFactor
*
minBufferSize
)
:
bufferSize
;
bufferSize
==
0
?
(
int
)
(
minBufferMultiplicationFactor
*
minBufferSize
)
:
bufferSize
;
this
.
sampleRate
=
sampleRate
;
this
.
sampleRate
=
sampleRate
;
this
.
channelConfig
=
channelConfig
;
this
.
channelConfig
=
channelConfig
;
this
.
isAc3
=
isAc3
;
ac3Bitrate
=
UNKNOWN_AC3_BITRATE
;
// Calculated on receiving the first buffer if isAc3 is true.
frameSize
=
2
*
channelCount
;
// 2 bytes per 16 bit sample * number of channels.
frameSize
=
2
*
channelCount
;
// 2 bytes per 16 bit sample * number of channels.
}
}
...
@@ -353,6 +364,14 @@ public final class AudioTrack {
...
@@ -353,6 +364,14 @@ public final class AudioTrack {
int
result
=
0
;
int
result
=
0
;
if
(
temporaryBufferSize
==
0
&&
size
!=
0
)
{
if
(
temporaryBufferSize
==
0
&&
size
!=
0
)
{
if
(
isAc3
&&
ac3Bitrate
==
UNKNOWN_AC3_BITRATE
)
{
// Each AC-3 buffer contains 1536 frames of audio, so the AudioTrack playback position
// advances by 1536 per buffer (32 ms at 48 kHz). Calculate the bitrate in kbit/s.
int
unscaledAc3Bitrate
=
size
*
8
*
sampleRate
;
int
divisor
=
1000
*
1536
;
ac3Bitrate
=
(
unscaledAc3Bitrate
+
divisor
/
2
)
/
divisor
;
}
// This is the first time we've seen this {@code buffer}.
// This is the first time we've seen this {@code buffer}.
// Note: presentationTimeUs corresponds to the end of the sample, not the start.
// Note: presentationTimeUs corresponds to the end of the sample, not the start.
long
bufferStartTime
=
presentationTimeUs
-
framesToDurationUs
(
bytesToFrames
(
size
));
long
bufferStartTime
=
presentationTimeUs
-
framesToDurationUs
(
bytesToFrames
(
size
));
...
@@ -616,19 +635,24 @@ public final class AudioTrack {
...
@@ -616,19 +635,24 @@ public final class AudioTrack {
}
}
private
long
framesToBytes
(
long
frameCount
)
{
private
long
framesToBytes
(
long
frameCount
)
{
// This method is unused on SDK >= 21.
return
frameCount
*
frameSize
;
return
frameCount
*
frameSize
;
}
}
private
long
bytesToFrames
(
long
byteCount
)
{
private
long
bytesToFrames
(
long
byteCount
)
{
if
(
isAc3
)
{
return
byteCount
*
8
*
sampleRate
/
(
1000
*
ac3Bitrate
);
}
else
{
return
byteCount
/
frameSize
;
return
byteCount
/
frameSize
;
}
}
}
private
long
framesToDurationUs
(
long
frameCount
)
{
private
long
framesToDurationUs
(
long
frameCount
)
{
return
(
frameCount
*
MICROS_PER_SECOND
)
/
sampleRate
;
return
(
frameCount
*
C
.
MICROS_PER_SECOND
)
/
sampleRate
;
}
}
private
long
durationUsToFrames
(
long
durationUs
)
{
private
long
durationUsToFrames
(
long
durationUs
)
{
return
(
durationUs
*
sampleRate
)
/
MICROS_PER_SECOND
;
return
(
durationUs
*
sampleRate
)
/
C
.
MICROS_PER_SECOND
;
}
}
private
void
resetSyncParams
()
{
private
void
resetSyncParams
()
{
...
...
library/src/main/java/com/google/android/exoplayer/chunk/MultiTrackChunkSource.java
View file @
511dd943
...
@@ -46,6 +46,10 @@ public class MultiTrackChunkSource implements ChunkSource, ExoPlayerComponent {
...
@@ -46,6 +46,10 @@ public class MultiTrackChunkSource implements ChunkSource, ExoPlayerComponent {
this
.
selectedSource
=
sources
[
0
];
this
.
selectedSource
=
sources
[
0
];
}
}
public
MultiTrackChunkSource
(
List
<
ChunkSource
>
sources
)
{
this
(
toChunkSourceArray
(
sources
));
}
/**
/**
* Gets the number of tracks that this source can switch between. May be called safely from any
* Gets the number of tracks that this source can switch between. May be called safely from any
* thread.
* thread.
...
@@ -107,4 +111,10 @@ public class MultiTrackChunkSource implements ChunkSource, ExoPlayerComponent {
...
@@ -107,4 +111,10 @@ public class MultiTrackChunkSource implements ChunkSource, ExoPlayerComponent {
selectedSource
.
onChunkLoadError
(
chunk
,
e
);
selectedSource
.
onChunkLoadError
(
chunk
,
e
);
}
}
private
static
ChunkSource
[]
toChunkSourceArray
(
List
<
ChunkSource
>
sources
)
{
ChunkSource
[]
chunkSourceArray
=
new
ChunkSource
[
sources
.
size
()];
sources
.
toArray
(
chunkSourceArray
);
return
chunkSourceArray
;
}
}
}
library/src/main/java/com/google/android/exoplayer/chunk/SingleSampleChunkSource.java
View file @
511dd943
...
@@ -15,6 +15,7 @@
...
@@ -15,6 +15,7 @@
*/
*/
package
com
.
google
.
android
.
exoplayer
.
chunk
;
package
com
.
google
.
android
.
exoplayer
.
chunk
;
import
com.google.android.exoplayer.C
;
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.upstream.DataSource
;
import
com.google.android.exoplayer.upstream.DataSource
;
...
@@ -42,7 +43,8 @@ public class SingleSampleChunkSource implements ChunkSource {
...
@@ -42,7 +43,8 @@ public class SingleSampleChunkSource implements ChunkSource {
* @param dataSource A {@link DataSource} suitable for loading the sample data.
* @param dataSource A {@link DataSource} suitable for loading the sample data.
* @param dataSpec Defines the location of the sample.
* @param dataSpec Defines the location of the sample.
* @param format The format of the sample.
* @param format The format of the sample.
* @param durationUs The duration of the sample in microseconds.
* @param durationUs The duration of the sample in microseconds, or {@link C#UNKNOWN_TIME_US} if
* the duration is unknown.
* @param mediaFormat The sample media format. May be null.
* @param mediaFormat The sample media format. May be null.
*/
*/
public
SingleSampleChunkSource
(
DataSource
dataSource
,
DataSpec
dataSpec
,
Format
format
,
public
SingleSampleChunkSource
(
DataSource
dataSource
,
DataSpec
dataSpec
,
Format
format
,
...
...
library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java
View file @
511dd943
...
@@ -15,9 +15,11 @@
...
@@ -15,9 +15,11 @@
*/
*/
package
com
.
google
.
android
.
exoplayer
.
dash
;
package
com
.
google
.
android
.
exoplayer
.
dash
;
import
com.google.android.exoplayer.BehindLiveWindowException
;
import
com.google.android.exoplayer.MediaFormat
;
import
com.google.android.exoplayer.MediaFormat
;
import
com.google.android.exoplayer.ParserException
;
import
com.google.android.exoplayer.ParserException
;
import
com.google.android.exoplayer.TrackInfo
;
import
com.google.android.exoplayer.TrackInfo
;
import
com.google.android.exoplayer.TrackRenderer
;
import
com.google.android.exoplayer.chunk.Chunk
;
import
com.google.android.exoplayer.chunk.Chunk
;
import
com.google.android.exoplayer.chunk.ChunkOperationHolder
;
import
com.google.android.exoplayer.chunk.ChunkOperationHolder
;
import
com.google.android.exoplayer.chunk.ChunkSource
;
import
com.google.android.exoplayer.chunk.ChunkSource
;
...
@@ -27,74 +29,175 @@ import com.google.android.exoplayer.chunk.FormatEvaluator;
...
@@ -27,74 +29,175 @@ import com.google.android.exoplayer.chunk.FormatEvaluator;
import
com.google.android.exoplayer.chunk.FormatEvaluator.Evaluation
;
import
com.google.android.exoplayer.chunk.FormatEvaluator.Evaluation
;
import
com.google.android.exoplayer.chunk.MediaChunk
;
import
com.google.android.exoplayer.chunk.MediaChunk
;
import
com.google.android.exoplayer.chunk.Mp4MediaChunk
;
import
com.google.android.exoplayer.chunk.Mp4MediaChunk
;
import
com.google.android.exoplayer.chunk.SingleSampleMediaChunk
;
import
com.google.android.exoplayer.dash.mpd.AdaptationSet
;
import
com.google.android.exoplayer.dash.mpd.MediaPresentationDescription
;
import
com.google.android.exoplayer.dash.mpd.Period
;
import
com.google.android.exoplayer.dash.mpd.RangedUri
;
import
com.google.android.exoplayer.dash.mpd.RangedUri
;
import
com.google.android.exoplayer.dash.mpd.Representation
;
import
com.google.android.exoplayer.dash.mpd.Representation
;
import
com.google.android.exoplayer.parser.Extractor
;
import
com.google.android.exoplayer.parser.Extractor
;
import
com.google.android.exoplayer.parser.mp4.FragmentedMp4Extractor
;
import
com.google.android.exoplayer.parser.mp4.FragmentedMp4Extractor
;
import
com.google.android.exoplayer.parser.webm.WebmExtractor
;
import
com.google.android.exoplayer.parser.webm.WebmExtractor
;
import
com.google.android.exoplayer.text.webvtt.WebvttParser
;
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.upstream.NonBlockingInputStream
;
import
com.google.android.exoplayer.upstream.NonBlockingInputStream
;
import
com.google.android.exoplayer.util.ManifestFetcher
;
import
com.google.android.exoplayer.util.MimeTypes
;
import
com.google.android.exoplayer.util.MimeTypes
;
import
android.net.Uri
;
import
android.net.Uri
;
import
android.os.SystemClock
;
import
java.io.IOException
;
import
java.io.IOException
;
import
java.util.Arrays
;
import
java.util.Arrays
;
import
java.util.Collections
;
import
java.util.HashMap
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.List
;
/**
/**
* An {@link ChunkSource} for DASH streams.
* An {@link ChunkSource} for DASH streams.
* <p>
* <p>
* This implementation currently supports fMP4
and webm
.
* This implementation currently supports fMP4
, webm, and webvtt
.
*/
*/
public
class
DashChunkSource
implements
ChunkSource
{
public
class
DashChunkSource
implements
ChunkSource
{
/**
* Thrown when an AdaptationSet is missing from the MPD.
*/
public
static
class
NoAdaptationSetException
extends
IOException
{
public
NoAdaptationSetException
(
String
message
)
{
super
(
message
);
}
}
/**
* Specifies that we should process all tracks.
*/
public
static
final
int
USE_ALL_TRACKS
=
-
1
;
private
final
TrackInfo
trackInfo
;
private
final
TrackInfo
trackInfo
;
private
final
DataSource
dataSource
;
private
final
DataSource
dataSource
;
private
final
FormatEvaluator
evaluator
;
private
final
FormatEvaluator
evaluator
;
private
final
Evaluation
evaluation
;
private
final
Evaluation
evaluation
;
private
final
StringBuilder
headerBuilder
;
private
final
long
liveEdgeLatencyUs
;
private
final
int
maxWidth
;
private
final
int
maxWidth
;
private
final
int
maxHeight
;
private
final
int
maxHeight
;
private
final
Format
[]
formats
;
private
final
Format
[]
formats
;
private
final
HashMap
<
String
,
Representation
>
representations
;
private
final
HashMap
<
String
,
RepresentationHolder
>
representationHolders
;
private
final
HashMap
<
String
,
Extractor
>
extractors
;
private
final
HashMap
<
String
,
DashSegmentIndex
>
segmentIndexes
;
private
final
ManifestFetcher
<
MediaPresentationDescription
>
manifestFetcher
;
private
final
int
adaptationSetIndex
;
private
final
int
[]
representationIndices
;
private
MediaPresentationDescription
currentManifest
;
private
boolean
finishedCurrentManifest
;
private
boolean
lastChunkWasInitialization
;
private
boolean
lastChunkWasInitialization
;
private
IOException
fatalError
;
/**
/**
* Lightweight constructor to use for fixed duration content.
*
* @param dataSource A {@link DataSource} suitable for loading the media data.
* @param dataSource A {@link DataSource} suitable for loading the media data.
* @param
e
valuator Selects from the available formats.
* @param
formatE
valuator Selects from the available formats.
* @param representations The representations to be considered by the source.
* @param representations The representations to be considered by the source.
*/
*/
public
DashChunkSource
(
DataSource
dataSource
,
FormatEvaluator
e
valuator
,
public
DashChunkSource
(
DataSource
dataSource
,
FormatEvaluator
formatE
valuator
,
Representation
...
representations
)
{
Representation
...
representations
)
{
this
(
buildManifest
(
Arrays
.
asList
(
representations
)),
0
,
null
,
dataSource
,
formatEvaluator
);
}
/**
* Lightweight constructor to use for fixed duration content.
*
* @param dataSource A {@link DataSource} suitable for loading the media data.
* @param formatEvaluator Selects from the available formats.
* @param representations The representations to be considered by the source.
*/
public
DashChunkSource
(
DataSource
dataSource
,
FormatEvaluator
formatEvaluator
,
List
<
Representation
>
representations
)
{
this
(
buildManifest
(
representations
),
0
,
null
,
dataSource
,
formatEvaluator
);
}
/**
* Constructor to use for fixed duration content.
*
* @param manifest The manifest.
* @param adaptationSetIndex The index of the adaptation set that should be used.
* @param representationIndices The indices of the representations within the adaptations set
* that should be used. May be null if all representations within the adaptation set should
* be considered.
* @param dataSource A {@link DataSource} suitable for loading the media data.
* @param formatEvaluator Selects from the available formats.
*/
public
DashChunkSource
(
MediaPresentationDescription
manifest
,
int
adaptationSetIndex
,
int
[]
representationIndices
,
DataSource
dataSource
,
FormatEvaluator
formatEvaluator
)
{
this
(
null
,
manifest
,
adaptationSetIndex
,
representationIndices
,
dataSource
,
formatEvaluator
,
0
);
}
/**
* 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 adaptationSetIndex The index of the adaptation set that should be used.
* @param representationIndices The indices of the representations within the adaptations set
* that should be used. May be null if all representations within the adaptation set 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
DashChunkSource
(
ManifestFetcher
<
MediaPresentationDescription
>
manifestFetcher
,
int
adaptationSetIndex
,
int
[]
representationIndices
,
DataSource
dataSource
,
FormatEvaluator
formatEvaluator
,
long
liveEdgeLatencyMs
)
{
this
(
manifestFetcher
,
manifestFetcher
.
getManifest
(),
adaptationSetIndex
,
representationIndices
,
dataSource
,
formatEvaluator
,
liveEdgeLatencyMs
*
1000
);
}
private
DashChunkSource
(
ManifestFetcher
<
MediaPresentationDescription
>
manifestFetcher
,
MediaPresentationDescription
initialManifest
,
int
adaptationSetIndex
,
int
[]
representationIndices
,
DataSource
dataSource
,
FormatEvaluator
formatEvaluator
,
long
liveEdgeLatencyUs
)
{
this
.
manifestFetcher
=
manifestFetcher
;
this
.
currentManifest
=
initialManifest
;
this
.
adaptationSetIndex
=
adaptationSetIndex
;
this
.
representationIndices
=
representationIndices
;
this
.
dataSource
=
dataSource
;
this
.
dataSource
=
dataSource
;
this
.
evaluator
=
evaluator
;
this
.
evaluator
=
formatEvaluator
;
this
.
formats
=
new
Format
[
representations
.
length
];
this
.
liveEdgeLatencyUs
=
liveEdgeLatencyUs
;
this
.
extractors
=
new
HashMap
<
String
,
Extractor
>();
this
.
segmentIndexes
=
new
HashMap
<
String
,
DashSegmentIndex
>();
this
.
representations
=
new
HashMap
<
String
,
Representation
>();
this
.
trackInfo
=
new
TrackInfo
(
representations
[
0
].
format
.
mimeType
,
representations
[
0
].
periodDurationMs
*
1000
);
this
.
evaluation
=
new
Evaluation
();
this
.
evaluation
=
new
Evaluation
();
this
.
headerBuilder
=
new
StringBuilder
();
Representation
[]
representations
=
getFilteredRepresentations
(
currentManifest
,
adaptationSetIndex
,
representationIndices
);
long
periodDurationUs
=
(
representations
[
0
].
periodDurationMs
==
TrackRenderer
.
UNKNOWN_TIME_US
)
?
TrackRenderer
.
UNKNOWN_TIME_US
:
representations
[
0
].
periodDurationMs
*
1000
;
this
.
trackInfo
=
new
TrackInfo
(
representations
[
0
].
format
.
mimeType
,
periodDurationUs
);
this
.
formats
=
new
Format
[
representations
.
length
];
this
.
representationHolders
=
new
HashMap
<
String
,
RepresentationHolder
>();
int
maxWidth
=
0
;
int
maxWidth
=
0
;
int
maxHeight
=
0
;
int
maxHeight
=
0
;
for
(
int
i
=
0
;
i
<
representations
.
length
;
i
++)
{
for
(
int
i
=
0
;
i
<
representations
.
length
;
i
++)
{
formats
[
i
]
=
representations
[
i
].
format
;
formats
[
i
]
=
representations
[
i
].
format
;
maxWidth
=
Math
.
max
(
formats
[
i
].
width
,
maxWidth
);
maxWidth
=
Math
.
max
(
formats
[
i
].
width
,
maxWidth
);
maxHeight
=
Math
.
max
(
formats
[
i
].
height
,
maxHeight
);
maxHeight
=
Math
.
max
(
formats
[
i
].
height
,
maxHeight
);
Extractor
extractor
=
mimeTypeIsWebm
(
formats
[
i
].
mimeType
)
Extractor
extractor
=
mimeTypeIsWebm
(
formats
[
i
].
mimeType
)
?
new
WebmExtractor
()
?
new
WebmExtractor
()
:
new
FragmentedMp4Extractor
();
:
new
FragmentedMp4Extractor
();
extractors
.
put
(
formats
[
i
].
id
,
extractor
);
representationHolders
.
put
(
formats
[
i
].
id
,
this
.
representations
.
put
(
formats
[
i
].
id
,
representations
[
i
]);
new
RepresentationHolder
(
representations
[
i
],
extractor
));
DashSegmentIndex
segmentIndex
=
representations
[
i
].
getIndex
();
if
(
segmentIndex
!=
null
)
{
segmentIndexes
.
put
(
formats
[
i
].
id
,
segmentIndex
);
}
}
}
this
.
maxWidth
=
maxWidth
;
this
.
maxWidth
=
maxWidth
;
this
.
maxHeight
=
maxHeight
;
this
.
maxHeight
=
maxHeight
;
...
@@ -116,21 +219,67 @@ public class DashChunkSource implements ChunkSource {
...
@@ -116,21 +219,67 @@ public class DashChunkSource implements ChunkSource {
@Override
@Override
public
void
enable
()
{
public
void
enable
()
{
evaluator
.
enable
();
evaluator
.
enable
();
if
(
manifestFetcher
!=
null
)
{
manifestFetcher
.
enable
();
}
}
}
@Override
@Override
public
void
disable
(
List
<?
extends
MediaChunk
>
queue
)
{
public
void
disable
(
List
<?
extends
MediaChunk
>
queue
)
{
evaluator
.
disable
();
evaluator
.
disable
();
if
(
manifestFetcher
!=
null
)
{
manifestFetcher
.
disable
();
}
}
}
@Override
@Override
public
void
continueBuffering
(
long
playbackPositionUs
)
{
public
void
continueBuffering
(
long
playbackPositionUs
)
{
// Do nothing
if
(
manifestFetcher
==
null
||
!
currentManifest
.
dynamic
||
fatalError
!=
null
)
{
return
;
}
MediaPresentationDescription
newManifest
=
manifestFetcher
.
getManifest
();
if
(
currentManifest
!=
newManifest
&&
newManifest
!=
null
)
{
Representation
[]
newRepresentations
=
DashChunkSource
.
getFilteredRepresentations
(
newManifest
,
adaptationSetIndex
,
representationIndices
);
for
(
Representation
representation
:
newRepresentations
)
{
RepresentationHolder
representationHolder
=
representationHolders
.
get
(
representation
.
format
.
id
);
DashSegmentIndex
oldIndex
=
representationHolder
.
segmentIndex
;
DashSegmentIndex
newIndex
=
representation
.
getIndex
();
int
newFirstSegmentNum
=
newIndex
.
getFirstSegmentNum
();
int
segmentNumShift
=
oldIndex
.
getSegmentNum
(
newIndex
.
getTimeUs
(
newFirstSegmentNum
))
-
newFirstSegmentNum
;
representationHolder
.
segmentNumShift
+=
segmentNumShift
;
representationHolder
.
segmentIndex
=
newIndex
;
}
currentManifest
=
newManifest
;
finishedCurrentManifest
=
false
;
}
// TODO: This is a temporary hack to avoid constantly refreshing the MPD in cases where
// minUpdatePeriod is set to 0. In such cases we shouldn't refresh unless there is explicit
// signaling in the stream, according to:
// http://azure.microsoft.com/blog/2014/09/13/dash-live-streaming-with-azure-media-service/
long
minUpdatePeriod
=
currentManifest
.
minUpdatePeriod
;
if
(
minUpdatePeriod
==
0
)
{
minUpdatePeriod
=
5000
;
}
if
(
finishedCurrentManifest
&&
(
SystemClock
.
elapsedRealtime
()
>
manifestFetcher
.
getManifestLoadTimestamp
()
+
minUpdatePeriod
))
{
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
();
if
(
evaluation
.
format
==
null
||
!
lastChunkWasInitialization
)
{
if
(
evaluation
.
format
==
null
||
!
lastChunkWasInitialization
)
{
evaluator
.
evaluate
(
queue
,
playbackPositionUs
,
formats
,
evaluation
);
evaluator
.
evaluate
(
queue
,
playbackPositionUs
,
formats
,
evaluation
);
...
@@ -148,17 +297,21 @@ public class DashChunkSource implements ChunkSource {
...
@@ -148,17 +297,21 @@ public class DashChunkSource implements ChunkSource {
return
;
return
;
}
}
Representation
selectedRepresentation
=
representations
.
get
(
selectedFormat
.
id
);
RepresentationHolder
representationHolder
=
representationHolders
.
get
(
selectedFormat
.
id
);
Extractor
extractor
=
extractors
.
get
(
selectedRepresentation
.
format
.
id
);
Representation
selectedRepresentation
=
representationHolder
.
representation
;
DashSegmentIndex
segmentIndex
=
representationHolder
.
segmentIndex
;
Extractor
extractor
=
representationHolder
.
extractor
;
RangedUri
pendingInitializationUri
=
null
;
RangedUri
pendingInitializationUri
=
null
;
RangedUri
pendingIndexUri
=
null
;
RangedUri
pendingIndexUri
=
null
;
if
(
extractor
.
getFormat
()
==
null
)
{
if
(
extractor
.
getFormat
()
==
null
)
{
pendingInitializationUri
=
selectedRepresentation
.
getInitializationUri
();
pendingInitializationUri
=
selectedRepresentation
.
getInitializationUri
();
}
}
if
(
!
segmentIndexes
.
containsKey
(
selectedRepresentation
.
format
.
id
)
)
{
if
(
segmentIndex
==
null
)
{
pendingIndexUri
=
selectedRepresentation
.
getIndexUri
();
pendingIndexUri
=
selectedRepresentation
.
getIndexUri
();
}
}
if
(
pendingInitializationUri
!=
null
||
pendingIndexUri
!=
null
)
{
if
(
pendingInitializationUri
!=
null
||
pendingIndexUri
!=
null
)
{
// We have initialization and/or index requests to make.
// We have initialization and/or index requests to make.
Chunk
initializationChunk
=
newInitializationChunk
(
pendingInitializationUri
,
pendingIndexUri
,
Chunk
initializationChunk
=
newInitializationChunk
(
pendingInitializationUri
,
pendingIndexUri
,
...
@@ -168,28 +321,48 @@ public class DashChunkSource implements ChunkSource {
...
@@ -168,28 +321,48 @@ public class DashChunkSource implements ChunkSource {
return
;
return
;
}
}
int
nextSegmentNum
;
int
segmentNum
;
DashSegmentIndex
segmentIndex
=
segmentIndexes
.
get
(
selectedRepresentation
.
format
.
id
);
if
(
queue
.
isEmpty
())
{
if
(
queue
.
isEmpty
())
{
nextSegmentNum
=
segmentIndex
.
getSegmentNum
(
seekPositionUs
);
if
(
currentManifest
.
dynamic
)
{
seekPositionUs
=
getLiveSeekPosition
();
}
segmentNum
=
segmentIndex
.
getSegmentNum
(
seekPositionUs
);
}
else
{
}
else
{
nextSegmentNum
=
queue
.
get
(
out
.
queueSize
-
1
).
nextChunkIndex
;
segmentNum
=
queue
.
get
(
out
.
queueSize
-
1
).
nextChunkIndex
-
representationHolder
.
segmentNumShift
;
}
if
(
currentManifest
.
dynamic
)
{
if
(
segmentNum
<
segmentIndex
.
getFirstSegmentNum
())
{
// This is before the first chunk in the current manifest.
fatalError
=
new
BehindLiveWindowException
();
return
;
}
else
if
(
segmentNum
>
segmentIndex
.
getLastSegmentNum
())
{
// This is beyond the last chunk in the current manifest.
finishedCurrentManifest
=
true
;
return
;
}
else
if
(
segmentNum
==
segmentIndex
.
getLastSegmentNum
())
{
// This is the last chunk in the current manifest. Mark the manifest as being finished,
// but continue to return the final chunk.
finishedCurrentManifest
=
true
;
}
}
}
if
(
nextS
egmentNum
==
-
1
)
{
if
(
s
egmentNum
==
-
1
)
{
out
.
chunk
=
null
;
out
.
chunk
=
null
;
return
;
return
;
}
}
Chunk
nextMediaChunk
=
newMediaChunk
(
selectedRepresentation
,
segmentIndex
,
extractor
,
Chunk
nextMediaChunk
=
newMediaChunk
(
representationHolder
,
dataSource
,
segmentNum
,
dataSource
,
nextSegmentNum
,
evaluation
.
trigger
);
evaluation
.
trigger
);
lastChunkWasInitialization
=
false
;
lastChunkWasInitialization
=
false
;
out
.
chunk
=
nextMediaChunk
;
out
.
chunk
=
nextMediaChunk
;
}
}
@Override
@Override
public
IOException
getError
()
{
public
IOException
getError
()
{
return
null
;
return
fatalError
!=
null
?
fatalError
:
(
manifestFetcher
!=
null
?
manifestFetcher
.
getError
()
:
null
);
}
}
@Override
@Override
...
@@ -229,22 +402,90 @@ public class DashChunkSource implements ChunkSource {
...
@@ -229,22 +402,90 @@ public class DashChunkSource implements ChunkSource {
}
}
DataSpec
dataSpec
=
new
DataSpec
(
requestUri
.
getUri
(),
requestUri
.
start
,
requestUri
.
length
,
DataSpec
dataSpec
=
new
DataSpec
(
requestUri
.
getUri
(),
requestUri
.
start
,
requestUri
.
length
,
representation
.
getCacheKey
());
representation
.
getCacheKey
());
return
new
InitializationLoadable
(
dataSource
,
dataSpec
,
trigger
,
representation
.
format
,
return
new
InitializationLoadable
(
dataSource
,
dataSpec
,
trigger
,
representation
.
format
,
extractor
,
expectedExtractorResult
,
indexAnchor
);
extractor
,
expectedExtractorResult
,
indexAnchor
);
}
}
private
Chunk
newMediaChunk
(
Representation
representation
,
DashSegmentIndex
segmentIndex
,
private
Chunk
newMediaChunk
(
RepresentationHolder
representationHolder
,
DataSource
dataSource
,
Extractor
extractor
,
DataSource
dataSource
,
int
segmentNum
,
int
trigger
)
{
int
segmentNum
,
int
trigger
)
{
int
lastSegmentNum
=
segmentIndex
.
getLastSegmentNum
();
Representation
representation
=
representationHolder
.
representation
;
int
nextSegmentNum
=
segmentNum
==
lastSegmentNum
?
-
1
:
segmentNum
+
1
;
DashSegmentIndex
segmentIndex
=
representationHolder
.
segmentIndex
;
long
startTimeUs
=
segmentIndex
.
getTimeUs
(
segmentNum
);
long
startTimeUs
=
segmentIndex
.
getTimeUs
(
segmentNum
);
long
endTimeUs
=
segmentNum
<
lastSegmentNum
?
segmentIndex
.
getTimeUs
(
segmentNum
+
1
)
long
endTimeUs
=
startTimeUs
+
segmentIndex
.
getDurationUs
(
segmentNum
);
:
startTimeUs
+
segmentIndex
.
getDurationUs
(
segmentNum
);
boolean
isLastSegment
=
!
currentManifest
.
dynamic
&&
segmentNum
==
segmentIndex
.
getLastSegmentNum
();
int
nextAbsoluteSegmentNum
=
isLastSegment
?
-
1
:
(
representationHolder
.
segmentNumShift
+
segmentNum
+
1
);
RangedUri
segmentUri
=
segmentIndex
.
getSegmentUrl
(
segmentNum
);
RangedUri
segmentUri
=
segmentIndex
.
getSegmentUrl
(
segmentNum
);
DataSpec
dataSpec
=
new
DataSpec
(
segmentUri
.
getUri
(),
segmentUri
.
start
,
segmentUri
.
length
,
DataSpec
dataSpec
=
new
DataSpec
(
segmentUri
.
getUri
(),
segmentUri
.
start
,
segmentUri
.
length
,
representation
.
getCacheKey
());
representation
.
getCacheKey
());
long
presentationTimeOffsetUs
=
representation
.
presentationTimeOffsetMs
*
1000
;
if
(
representation
.
format
.
mimeType
.
equals
(
MimeTypes
.
TEXT_VTT
))
{
if
(
representationHolder
.
vttHeaderOffsetUs
!=
presentationTimeOffsetUs
)
{
// Update the VTT header.
headerBuilder
.
setLength
(
0
);
headerBuilder
.
append
(
WebvttParser
.
EXO_HEADER
).
append
(
"="
)
.
append
(
WebvttParser
.
OFFSET
).
append
(
presentationTimeOffsetUs
).
append
(
"\n"
);
representationHolder
.
vttHeader
=
headerBuilder
.
toString
().
getBytes
();
representationHolder
.
vttHeaderOffsetUs
=
presentationTimeOffsetUs
;
}
return
new
SingleSampleMediaChunk
(
dataSource
,
dataSpec
,
representation
.
format
,
0
,
startTimeUs
,
endTimeUs
,
nextAbsoluteSegmentNum
,
null
,
representationHolder
.
vttHeader
);
}
else
{
return
new
Mp4MediaChunk
(
dataSource
,
dataSpec
,
representation
.
format
,
trigger
,
startTimeUs
,
return
new
Mp4MediaChunk
(
dataSource
,
dataSpec
,
representation
.
format
,
trigger
,
startTimeUs
,
endTimeUs
,
nextSegmentNum
,
extractor
,
false
,
0
);
endTimeUs
,
nextAbsoluteSegmentNum
,
representationHolder
.
extractor
,
false
,
presentationTimeOffsetUs
);
}
}
/**
* 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
(
RepresentationHolder
representationHolder
:
representationHolders
.
values
())
{
DashSegmentIndex
segmentIndex
=
representationHolder
.
segmentIndex
;
int
lastSegmentNum
=
segmentIndex
.
getLastSegmentNum
();
long
indexLiveEdgeTimestampUs
=
segmentIndex
.
getTimeUs
(
lastSegmentNum
)
+
segmentIndex
.
getDurationUs
(
lastSegmentNum
);
liveEdgeTimestampUs
=
Math
.
max
(
liveEdgeTimestampUs
,
indexLiveEdgeTimestampUs
);
}
return
liveEdgeTimestampUs
-
liveEdgeLatencyUs
;
}
private
static
Representation
[]
getFilteredRepresentations
(
MediaPresentationDescription
manifest
,
int
adaptationSetIndex
,
int
[]
representationIndices
)
{
List
<
Representation
>
representations
=
manifest
.
periods
.
get
(
0
).
adaptationSets
.
get
(
adaptationSetIndex
).
representations
;
if
(
representationIndices
==
null
)
{
Representation
[]
filteredRepresentations
=
new
Representation
[
representations
.
size
()];
representations
.
toArray
(
filteredRepresentations
);
return
filteredRepresentations
;
}
else
{
Representation
[]
filteredRepresentations
=
new
Representation
[
representationIndices
.
length
];
for
(
int
i
=
0
;
i
<
representationIndices
.
length
;
i
++)
{
filteredRepresentations
[
i
]
=
representations
.
get
(
representationIndices
[
i
]);
}
return
filteredRepresentations
;
}
}
private
static
MediaPresentationDescription
buildManifest
(
List
<
Representation
>
representations
)
{
Representation
firstRepresentation
=
representations
.
get
(
0
);
AdaptationSet
adaptationSet
=
new
AdaptationSet
(
0
,
AdaptationSet
.
TYPE_UNKNOWN
,
representations
);
Period
period
=
new
Period
(
null
,
firstRepresentation
.
periodStartMs
,
firstRepresentation
.
periodDurationMs
,
Collections
.
singletonList
(
adaptationSet
));
long
duration
=
firstRepresentation
.
periodDurationMs
-
firstRepresentation
.
periodStartMs
;
return
new
MediaPresentationDescription
(-
1
,
duration
,
-
1
,
false
,
-
1
,
-
1
,
null
,
Collections
.
singletonList
(
period
));
}
}
private
class
InitializationLoadable
extends
Chunk
{
private
class
InitializationLoadable
extends
Chunk
{
...
@@ -272,9 +513,28 @@ public class DashChunkSource implements ChunkSource {
...
@@ -272,9 +513,28 @@ public class DashChunkSource implements ChunkSource {
+
expectedExtractorResult
+
", got "
+
result
);
+
expectedExtractorResult
+
", got "
+
result
);
}
}
if
((
result
&
Extractor
.
RESULT_READ_INDEX
)
!=
0
)
{
if
((
result
&
Extractor
.
RESULT_READ_INDEX
)
!=
0
)
{
segmentIndexes
.
put
(
format
.
id
,
representationHolders
.
get
(
format
.
id
).
segmentIndex
=
new
DashWrappingSegmentIndex
(
extractor
.
getIndex
(),
uri
,
indexAnchor
));
new
DashWrappingSegmentIndex
(
extractor
.
getIndex
(),
uri
,
indexAnchor
);
}
}
}
}
private
static
class
RepresentationHolder
{
public
final
Representation
representation
;
public
final
Extractor
extractor
;
public
DashSegmentIndex
segmentIndex
;
public
int
segmentNumShift
;
public
long
vttHeaderOffsetUs
;
public
byte
[]
vttHeader
;
public
RepresentationHolder
(
Representation
representation
,
Extractor
extractor
)
{
this
.
representation
=
representation
;
this
.
extractor
=
extractor
;
this
.
segmentIndex
=
representation
.
getIndex
();
}
}
}
}
...
...
library/src/main/java/com/google/android/exoplayer/dash/mpd/Period.java
View file @
511dd943
...
@@ -56,4 +56,21 @@ public class Period {
...
@@ -56,4 +56,21 @@ public class Period {
this
.
adaptationSets
=
Collections
.
unmodifiableList
(
adaptationSets
);
this
.
adaptationSets
=
Collections
.
unmodifiableList
(
adaptationSets
);
}
}
/**
* Returns the index of the first adaptation set of a given type, or -1 if no adaptation set of
* the specified type exists.
*
* @param type An adaptation set type.
* @return The index of the first adaptation set of the specified type, or -1.
*/
public
int
getAdaptationSetIndex
(
int
type
)
{
int
adaptationCount
=
adaptationSets
.
size
();
for
(
int
i
=
0
;
i
<
adaptationCount
;
i
++)
{
if
(
adaptationSets
.
get
(
i
).
type
==
type
)
{
return
i
;
}
}
return
-
1
;
}
}
}
library/src/main/java/com/google/android/exoplayer/dash/mpd/SegmentBase.java
View file @
511dd943
...
@@ -15,6 +15,7 @@
...
@@ -15,6 +15,7 @@
*/
*/
package
com
.
google
.
android
.
exoplayer
.
dash
.
mpd
;
package
com
.
google
.
android
.
exoplayer
.
dash
.
mpd
;
import
com.google.android.exoplayer.C
;
import
com.google.android.exoplayer.util.Util
;
import
com.google.android.exoplayer.util.Util
;
import
android.net.Uri
;
import
android.net.Uri
;
...
@@ -141,11 +142,12 @@ public abstract class SegmentBase {
...
@@ -141,11 +142,12 @@ public abstract class SegmentBase {
public
final
long
getSegmentDurationUs
(
int
sequenceNumber
)
{
public
final
long
getSegmentDurationUs
(
int
sequenceNumber
)
{
if
(
segmentTimeline
!=
null
)
{
if
(
segmentTimeline
!=
null
)
{
return
(
segmentTimeline
.
get
(
sequenceNumber
-
startNumber
).
duration
*
1000000
)
/
timescale
;
long
duration
=
segmentTimeline
.
get
(
sequenceNumber
-
startNumber
).
duration
;
return
(
duration
*
C
.
MICROS_PER_SECOND
)
/
timescale
;
}
else
{
}
else
{
return
sequenceNumber
==
getLastSegmentNum
()
return
sequenceNumber
==
getLastSegmentNum
()
?
(
periodDurationMs
*
1000
)
-
getSegmentTimeUs
(
sequenceNumber
)
?
(
(
periodDurationMs
*
1000
)
-
getSegmentTimeUs
(
sequenceNumber
)
)
:
((
duration
*
1000000L
)
/
timescale
);
:
((
duration
*
C
.
MICROS_PER_SECOND
)
/
timescale
);
}
}
}
}
...
@@ -157,7 +159,7 @@ public abstract class SegmentBase {
...
@@ -157,7 +159,7 @@ public abstract class SegmentBase {
}
else
{
}
else
{
unscaledSegmentTime
=
(
sequenceNumber
-
startNumber
)
*
duration
;
unscaledSegmentTime
=
(
sequenceNumber
-
startNumber
)
*
duration
;
}
}
return
Util
.
scaleLargeTimestamp
(
unscaledSegmentTime
,
1000000
,
timescale
);
return
Util
.
scaleLargeTimestamp
(
unscaledSegmentTime
,
C
.
MICROS_PER_SECOND
,
timescale
);
}
}
public
abstract
RangedUri
getSegmentUrl
(
Representation
representation
,
int
index
);
public
abstract
RangedUri
getSegmentUrl
(
Representation
representation
,
int
index
);
...
...
library/src/main/java/com/google/android/exoplayer/parser/mp4/FragmentedMp4Extractor.java
View file @
511dd943
...
@@ -15,6 +15,7 @@
...
@@ -15,6 +15,7 @@
*/
*/
package
com
.
google
.
android
.
exoplayer
.
parser
.
mp4
;
package
com
.
google
.
android
.
exoplayer
.
parser
.
mp4
;
import
com.google.android.exoplayer.C
;
import
com.google.android.exoplayer.MediaFormat
;
import
com.google.android.exoplayer.MediaFormat
;
import
com.google.android.exoplayer.ParserException
;
import
com.google.android.exoplayer.ParserException
;
import
com.google.android.exoplayer.SampleHolder
;
import
com.google.android.exoplayer.SampleHolder
;
...
@@ -26,6 +27,7 @@ import com.google.android.exoplayer.upstream.NonBlockingInputStream;
...
@@ -26,6 +27,7 @@ import com.google.android.exoplayer.upstream.NonBlockingInputStream;
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.MimeTypes
;
import
com.google.android.exoplayer.util.MimeTypes
;
import
com.google.android.exoplayer.util.Util
;
import
android.annotation.SuppressLint
;
import
android.annotation.SuppressLint
;
import
android.media.MediaCodec
;
import
android.media.MediaCodec
;
...
@@ -1053,6 +1055,7 @@ public final class FragmentedMp4Extractor implements Extractor {
...
@@ -1053,6 +1055,7 @@ public final class FragmentedMp4Extractor implements Extractor {
long
offset
=
firstOffset
;
long
offset
=
firstOffset
;
long
time
=
earliestPresentationTime
;
long
time
=
earliestPresentationTime
;
long
timeUs
=
Util
.
scaleLargeTimestamp
(
time
,
C
.
MICROS_PER_SECOND
,
timescale
);
for
(
int
i
=
0
;
i
<
referenceCount
;
i
++)
{
for
(
int
i
=
0
;
i
<
referenceCount
;
i
++)
{
int
firstInt
=
atom
.
readInt
();
int
firstInt
=
atom
.
readInt
();
...
@@ -1067,10 +1070,10 @@ public final class FragmentedMp4Extractor implements Extractor {
...
@@ -1067,10 +1070,10 @@ public final class FragmentedMp4Extractor implements Extractor {
// Calculate time and duration values such that any rounding errors are consistent. i.e. That
// Calculate time and duration values such that any rounding errors are consistent. i.e. That
// timesUs[i] + durationsUs[i] == timesUs[i + 1].
// timesUs[i] + durationsUs[i] == timesUs[i + 1].
timesUs
[
i
]
=
(
time
*
1000000L
)
/
timescale
;
timesUs
[
i
]
=
timeUs
;
long
nextTimeUs
=
((
time
+
referenceDuration
)
*
1000000L
)
/
timescale
;
durationsUs
[
i
]
=
nextTimeUs
-
timesUs
[
i
];
time
+=
referenceDuration
;
time
+=
referenceDuration
;
timeUs
=
Util
.
scaleLargeTimestamp
(
time
,
C
.
MICROS_PER_SECOND
,
timescale
);
durationsUs
[
i
]
=
timeUs
-
timesUs
[
i
];
atom
.
skip
(
4
);
atom
.
skip
(
4
);
offset
+=
sizes
[
i
];
offset
+=
sizes
[
i
];
...
...
library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingManifest.java
View file @
511dd943
...
@@ -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.C
;
import
com.google.android.exoplayer.util.Assertions
;
import
com.google.android.exoplayer.util.Assertions
;
import
com.google.android.exoplayer.util.Util
;
import
com.google.android.exoplayer.util.Util
;
...
@@ -31,30 +32,77 @@ import java.util.UUID;
...
@@ -31,30 +32,77 @@ import java.util.UUID;
*/
*/
public
class
SmoothStreamingManifest
{
public
class
SmoothStreamingManifest
{
private
static
final
long
MICROS_PER_SECOND
=
1000000L
;
/**
* The client manifest major version.
*/
public
final
int
majorVersion
;
public
final
int
majorVersion
;
/**
* The client manifest minor version.
*/
public
final
int
minorVersion
;
public
final
int
minorVersion
;
public
final
long
timescale
;
/**
* The number of fragments in a lookahead, or -1 if the lookahead is unspecified.
*/
public
final
int
lookAheadCount
;
public
final
int
lookAheadCount
;
/**
* True if the manifest describes a live presentation still in progress. False otherwise.
*/
public
final
boolean
isLive
;
public
final
boolean
isLive
;
/**
* Content protection information, or null if the content is not protected.
*/
public
final
ProtectionElement
protectionElement
;
public
final
ProtectionElement
protectionElement
;
/**
* The contained stream elements.
*/
public
final
StreamElement
[]
streamElements
;
public
final
StreamElement
[]
streamElements
;
/**
* The overall presentation duration of the media in microseconds, or {@link C#UNKNOWN_TIME_US}
* if the duration is unknown.
*/
public
final
long
durationUs
;
public
final
long
durationUs
;
/**
* The length of the trailing window for a live broadcast in microseconds, or
* {@link C#UNKNOWN_TIME_US} if the stream is not live or if the window length is unspecified.
*/
public
final
long
dvrWindowLengthUs
;
public
final
long
dvrWindowLengthUs
;
/**
* @param majorVersion The client manifest major version.
* @param minorVersion The client manifest minor version.
* @param timescale The timescale of the media as the number of units that pass in one second.
* @param duration The overall presentation duration in units of the timescale attribute, or 0
* if the duration is unknown.
* @param dvrWindowLength The length of the trailing window in units of the timescale attribute,
* or 0 if this attribute is unspecified or not applicable.
* @param lookAheadCount The number of fragments in a lookahead, or -1 if this attribute is
* unspecified or not applicable.
* @param isLive True if the manifest describes a live presentation still in progress. False
* otherwise.
* @param protectionElement Content protection information, or null if the content is not
* protected.
* @param streamElements The contained stream elements.
*/
public
SmoothStreamingManifest
(
int
majorVersion
,
int
minorVersion
,
long
timescale
,
long
duration
,
public
SmoothStreamingManifest
(
int
majorVersion
,
int
minorVersion
,
long
timescale
,
long
duration
,
long
dvrWindowLength
,
int
lookAheadCount
,
boolean
isLive
,
ProtectionElement
protectionElement
,
long
dvrWindowLength
,
int
lookAheadCount
,
boolean
isLive
,
ProtectionElement
protectionElement
,
StreamElement
[]
streamElements
)
{
StreamElement
[]
streamElements
)
{
this
.
majorVersion
=
majorVersion
;
this
.
majorVersion
=
majorVersion
;
this
.
minorVersion
=
minorVersion
;
this
.
minorVersion
=
minorVersion
;
this
.
timescale
=
timescale
;
this
.
lookAheadCount
=
lookAheadCount
;
this
.
lookAheadCount
=
lookAheadCount
;
this
.
isLive
=
isLive
;
this
.
isLive
=
isLive
;
this
.
protectionElement
=
protectionElement
;
this
.
protectionElement
=
protectionElement
;
this
.
streamElements
=
streamElements
;
this
.
streamElements
=
streamElements
;
dvrWindowLengthUs
=
Util
.
scaleLargeTimestamp
(
dvrWindowLength
,
MICROS_PER_SECOND
,
timescale
);
dvrWindowLengthUs
=
dvrWindowLength
==
0
?
C
.
UNKNOWN_TIME_US
durationUs
=
Util
.
scaleLargeTimestamp
(
duration
,
MICROS_PER_SECOND
,
timescale
);
:
Util
.
scaleLargeTimestamp
(
dvrWindowLength
,
C
.
MICROS_PER_SECOND
,
timescale
);
durationUs
=
duration
==
0
?
C
.
UNKNOWN_TIME_US
:
Util
.
scaleLargeTimestamp
(
duration
,
C
.
MICROS_PER_SECOND
,
timescale
);
}
}
/**
/**
...
@@ -176,9 +224,9 @@ public class SmoothStreamingManifest {
...
@@ -176,9 +224,9 @@ public class SmoothStreamingManifest {
this
.
chunkCount
=
chunkStartTimes
.
size
();
this
.
chunkCount
=
chunkStartTimes
.
size
();
this
.
chunkStartTimes
=
chunkStartTimes
;
this
.
chunkStartTimes
=
chunkStartTimes
;
lastChunkDurationUs
=
lastChunkDurationUs
=
Util
.
scaleLargeTimestamp
(
lastChunkDuration
,
MICROS_PER_SECOND
,
timescale
);
Util
.
scaleLargeTimestamp
(
lastChunkDuration
,
C
.
MICROS_PER_SECOND
,
timescale
);
chunkStartTimesUs
=
chunkStartTimesUs
=
Util
.
scaleLargeTimestamps
(
chunkStartTimes
,
MICROS_PER_SECOND
,
timescale
);
Util
.
scaleLargeTimestamps
(
chunkStartTimes
,
C
.
MICROS_PER_SECOND
,
timescale
);
}
}
/**
/**
...
...
library/src/main/java/com/google/android/exoplayer/text/ttml/TtmlParser.java
View file @
511dd943
...
@@ -15,6 +15,7 @@
...
@@ -15,6 +15,7 @@
*/
*/
package
com
.
google
.
android
.
exoplayer
.
text
.
ttml
;
package
com
.
google
.
android
.
exoplayer
.
text
.
ttml
;
import
com.google.android.exoplayer.C
;
import
com.google.android.exoplayer.ParserException
;
import
com.google.android.exoplayer.ParserException
;
import
com.google.android.exoplayer.text.Subtitle
;
import
com.google.android.exoplayer.text.Subtitle
;
import
com.google.android.exoplayer.text.SubtitleParser
;
import
com.google.android.exoplayer.text.SubtitleParser
;
...
@@ -254,7 +255,7 @@ public class TtmlParser implements SubtitleParser {
...
@@ -254,7 +255,7 @@ public class TtmlParser implements SubtitleParser {
String
subframes
=
matcher
.
group
(
6
);
String
subframes
=
matcher
.
group
(
6
);
durationSeconds
+=
(
subframes
!=
null
)
?
durationSeconds
+=
(
subframes
!=
null
)
?
((
double
)
Long
.
parseLong
(
subframes
))
/
subframeRate
/
frameRate
:
0
;
((
double
)
Long
.
parseLong
(
subframes
))
/
subframeRate
/
frameRate
:
0
;
return
(
long
)
(
durationSeconds
*
1000000
);
return
(
long
)
(
durationSeconds
*
C
.
MICROS_PER_SECOND
);
}
}
matcher
=
OFFSET_TIME
.
matcher
(
time
);
matcher
=
OFFSET_TIME
.
matcher
(
time
);
if
(
matcher
.
matches
())
{
if
(
matcher
.
matches
())
{
...
@@ -274,7 +275,7 @@ public class TtmlParser implements SubtitleParser {
...
@@ -274,7 +275,7 @@ public class TtmlParser implements SubtitleParser {
}
else
if
(
unit
.
equals
(
"t"
))
{
}
else
if
(
unit
.
equals
(
"t"
))
{
offsetSeconds
/=
tickRate
;
offsetSeconds
/=
tickRate
;
}
}
return
(
long
)
(
offsetSeconds
*
1000000
);
return
(
long
)
(
offsetSeconds
*
C
.
MICROS_PER_SECOND
);
}
}
throw
new
ParserException
(
"Malformed time expression: "
+
time
);
throw
new
ParserException
(
"Malformed time expression: "
+
time
);
}
}
...
...
library/src/main/java/com/google/android/exoplayer/util/ManifestFetcher.java
View file @
511dd943
...
@@ -18,6 +18,7 @@ package com.google.android.exoplayer.util;
...
@@ -18,6 +18,7 @@ package com.google.android.exoplayer.util;
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
android.os.Handler
;
import
android.os.Looper
;
import
android.os.Looper
;
import
android.os.SystemClock
;
import
android.os.SystemClock
;
import
android.util.Pair
;
import
android.util.Pair
;
...
@@ -29,13 +30,26 @@ import java.net.URLConnection;
...
@@ -29,13 +30,26 @@ import java.net.URLConnection;
import
java.util.concurrent.CancellationException
;
import
java.util.concurrent.CancellationException
;
/**
/**
* Performs both single and repeated loads of media man
f
ifests.
* Performs both single and repeated loads of media manifests.
*
*
* @param <T> The type of manifest.
* @param <T> The type of manifest.
*/
*/
public
class
ManifestFetcher
<
T
>
implements
Loader
.
Callback
{
public
class
ManifestFetcher
<
T
>
implements
Loader
.
Callback
{
/**
/**
* Interface definition for a callback to be notified of {@link ManifestFetcher} events.
*/
public
interface
EventListener
{
public
void
onManifestRefreshStarted
();
public
void
onManifestRefreshed
();
public
void
onManifestError
(
IOException
e
);
}
/**
* Callback for the result of a single load.
* Callback for the result of a single load.
*
*
* @param <T> The type of manifest.
* @param <T> The type of manifest.
...
@@ -61,9 +75,12 @@ public class ManifestFetcher<T> implements Loader.Callback {
...
@@ -61,9 +75,12 @@ public class ManifestFetcher<T> implements Loader.Callback {
}
}
/* package */
final
ManifestParser
<
T
>
parser
;
/* package */
final
ManifestParser
<
T
>
parser
;
/* package */
final
String
manifestUrl
;
/* package */
final
String
contentId
;
/* package */
final
String
contentId
;
/* package */
final
String
userAgent
;
/* package */
final
String
userAgent
;
private
final
Handler
eventHandler
;
private
final
EventListener
eventListener
;
/* package */
volatile
String
manifestUrl
;
private
int
enabledCount
;
private
int
enabledCount
;
private
Loader
loader
;
private
Loader
loader
;
...
@@ -76,6 +93,11 @@ public class ManifestFetcher<T> implements Loader.Callback {
...
@@ -76,6 +93,11 @@ 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
)
{
this
(
parser
,
contentId
,
manifestUrl
,
userAgent
,
null
,
null
);
}
/**
/**
* @param parser A parser to parse the loaded manifest data.
* @param parser A parser to parse the loaded manifest data.
* @param contentId The content id of the content being loaded. May be null.
* @param contentId The content id of the content being loaded. May be null.
...
@@ -83,11 +105,22 @@ public class ManifestFetcher<T> implements Loader.Callback {
...
@@ -83,11 +105,22 @@ public class ManifestFetcher<T> implements Loader.Callback {
* @param userAgent The User-Agent string that should be used.
* @param userAgent The User-Agent string that should be used.
*/
*/
public
ManifestFetcher
(
ManifestParser
<
T
>
parser
,
String
contentId
,
String
manifestUrl
,
public
ManifestFetcher
(
ManifestParser
<
T
>
parser
,
String
contentId
,
String
manifestUrl
,
String
userAgent
)
{
String
userAgent
,
Handler
eventHandler
,
EventListener
eventListener
)
{
this
.
parser
=
parser
;
this
.
parser
=
parser
;
this
.
contentId
=
contentId
;
this
.
contentId
=
contentId
;
this
.
manifestUrl
=
manifestUrl
;
this
.
manifestUrl
=
manifestUrl
;
this
.
userAgent
=
userAgent
;
this
.
userAgent
=
userAgent
;
this
.
eventHandler
=
eventHandler
;
this
.
eventListener
=
eventListener
;
}
/**
* Updates the manifest location.
*
* @param manifestUrl The manifest location.
*/
public
void
updateManifestUrl
(
String
manifestUrl
)
{
this
.
manifestUrl
=
manifestUrl
;
}
}
/**
/**
...
@@ -173,6 +206,7 @@ public class ManifestFetcher<T> implements Loader.Callback {
...
@@ -173,6 +206,7 @@ public class ManifestFetcher<T> implements Loader.Callback {
if
(!
loader
.
isLoading
())
{
if
(!
loader
.
isLoading
())
{
currentLoadable
=
new
ManifestLoadable
();
currentLoadable
=
new
ManifestLoadable
();
loader
.
startLoading
(
currentLoadable
,
this
);
loader
.
startLoading
(
currentLoadable
,
this
);
notifyManifestRefreshStarted
();
}
}
}
}
...
@@ -187,6 +221,8 @@ public class ManifestFetcher<T> implements Loader.Callback {
...
@@ -187,6 +221,8 @@ public class ManifestFetcher<T> implements Loader.Callback {
manifestLoadTimestamp
=
SystemClock
.
elapsedRealtime
();
manifestLoadTimestamp
=
SystemClock
.
elapsedRealtime
();
loadExceptionCount
=
0
;
loadExceptionCount
=
0
;
loadException
=
null
;
loadException
=
null
;
notifyManifestRefreshed
();
}
}
@Override
@Override
...
@@ -204,12 +240,47 @@ public class ManifestFetcher<T> implements Loader.Callback {
...
@@ -204,12 +240,47 @@ public class ManifestFetcher<T> implements Loader.Callback {
loadExceptionCount
++;
loadExceptionCount
++;
loadExceptionTimestamp
=
SystemClock
.
elapsedRealtime
();
loadExceptionTimestamp
=
SystemClock
.
elapsedRealtime
();
loadException
=
new
IOException
(
exception
);
loadException
=
new
IOException
(
exception
);
notifyManifestError
(
loadException
);
}
}
private
long
getRetryDelayMillis
(
long
errorCount
)
{
private
long
getRetryDelayMillis
(
long
errorCount
)
{
return
Math
.
min
((
errorCount
-
1
)
*
1000
,
5000
);
return
Math
.
min
((
errorCount
-
1
)
*
1000
,
5000
);
}
}
private
void
notifyManifestRefreshStarted
()
{
if
(
eventHandler
!=
null
&&
eventListener
!=
null
)
{
eventHandler
.
post
(
new
Runnable
()
{
@Override
public
void
run
()
{
eventListener
.
onManifestRefreshStarted
();
}
});
}
}
private
void
notifyManifestRefreshed
()
{
if
(
eventHandler
!=
null
&&
eventListener
!=
null
)
{
eventHandler
.
post
(
new
Runnable
()
{
@Override
public
void
run
()
{
eventListener
.
onManifestRefreshed
();
}
});
}
}
private
void
notifyManifestError
(
final
IOException
e
)
{
if
(
eventHandler
!=
null
&&
eventListener
!=
null
)
{
eventHandler
.
post
(
new
Runnable
()
{
@Override
public
void
run
()
{
eventListener
.
onManifestError
(
e
);
}
});
}
}
private
class
SingleFetchHelper
implements
Loader
.
Callback
{
private
class
SingleFetchHelper
implements
Loader
.
Callback
{
private
final
Looper
callbackLooper
;
private
final
Looper
callbackLooper
;
...
...
library/src/main/java/com/google/android/exoplayer/util/PlayerControl.java
View file @
511dd943
...
@@ -70,12 +70,14 @@ public class PlayerControl implements MediaPlayerControl {
...
@@ -70,12 +70,14 @@ public class PlayerControl implements MediaPlayerControl {
@Override
@Override
public
int
getCurrentPosition
()
{
public
int
getCurrentPosition
()
{
return
(
int
)
exoPlayer
.
getCurrentPosition
();
return
exoPlayer
.
getDuration
()
==
ExoPlayer
.
UNKNOWN_TIME
?
0
:
(
int
)
exoPlayer
.
getCurrentPosition
();
}
}
@Override
@Override
public
int
getDuration
()
{
public
int
getDuration
()
{
return
(
int
)
exoPlayer
.
getDuration
();
return
exoPlayer
.
getDuration
()
==
ExoPlayer
.
UNKNOWN_TIME
?
0
:
(
int
)
exoPlayer
.
getDuration
();
}
}
@Override
@Override
...
@@ -95,8 +97,9 @@ public class PlayerControl implements MediaPlayerControl {
...
@@ -95,8 +97,9 @@ public class PlayerControl implements MediaPlayerControl {
@Override
@Override
public
void
seekTo
(
int
timeMillis
)
{
public
void
seekTo
(
int
timeMillis
)
{
// MediaController arrow keys generate unbounded values.
long
seekPosition
=
exoPlayer
.
getDuration
()
==
ExoPlayer
.
UNKNOWN_TIME
?
0
exoPlayer
.
seekTo
(
Math
.
min
(
Math
.
max
(
0
,
timeMillis
),
getDuration
()));
:
Math
.
min
(
Math
.
max
(
0
,
timeMillis
),
getDuration
());
exoPlayer
.
seekTo
(
seekPosition
);
}
}
}
}
library/src/main/java/com/google/android/exoplayer/util/Util.java
View file @
511dd943
...
@@ -399,4 +399,22 @@ public final class Util {
...
@@ -399,4 +399,22 @@ public final class Util {
return
scaledTimestamps
;
return
scaledTimestamps
;
}
}
/**
* Converts a list of integers to a primitive array.
*
* @param list A list of integers.
* @return The list in array form, or null if the input list was null.
*/
public
static
int
[]
toArray
(
List
<
Integer
>
list
)
{
if
(
list
==
null
)
{
return
null
;
}
int
length
=
list
.
size
();
int
[]
intArray
=
new
int
[
length
];
for
(
int
i
=
0
;
i
<
length
;
i
++)
{
intArray
[
i
]
=
list
.
get
(
i
);
}
return
intArray
;
}
}
}
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