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
4c29eb58
authored
Dec 12, 2014
by
Oliver Woodman
Browse files
Options
_('Browse Files')
Download
Plain Diff
Merge branch 'dev' into dev-hls
parents
1554db16
0cb81693
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
22 changed files
with
305 additions
and
121 deletions
demo/src/main/java/com/google/android/exoplayer/demo/SampleChooserActivity.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/DashRendererBuilder.java
demo/src/main/java/com/google/android/exoplayer/demo/full/player/DemoPlayer.java
demo/src/main/java/com/google/android/exoplayer/demo/full/player/SmoothStreamingRendererBuilder.java
demo/src/main/java/com/google/android/exoplayer/demo/full/player/UnsupportedDrmException.java
demo/src/main/res/values/strings.xml
library/src/main/java/com/google/android/exoplayer/Ac3PassthroughAudioTrackRenderer.java
library/src/main/java/com/google/android/exoplayer/MediaFormat.java
library/src/main/java/com/google/android/exoplayer/TrackRenderer.java
library/src/main/java/com/google/android/exoplayer/chunk/ChunkSampleSource.java
library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java
library/src/main/java/com/google/android/exoplayer/dash/DashSegmentIndex.java
library/src/main/java/com/google/android/exoplayer/dash/mpd/MediaPresentationDescriptionParser.java
library/src/main/java/com/google/android/exoplayer/dash/mpd/SegmentBase.java
library/src/main/java/com/google/android/exoplayer/parser/mp4/Atom.java
library/src/main/java/com/google/android/exoplayer/parser/mp4/FragmentedMp4Extractor.java
library/src/main/java/com/google/android/exoplayer/parser/mp4/Track.java
library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingChunkSource.java
library/src/main/java/com/google/android/exoplayer/text/TextTrackRenderer.java
library/src/main/java/com/google/android/exoplayer/text/webvtt/WebvttParser.java
demo/src/main/java/com/google/android/exoplayer/demo/SampleChooserActivity.java
View file @
4c29eb58
...
...
@@ -18,7 +18,6 @@ package com.google.android.exoplayer.demo;
import
com.google.android.exoplayer.demo.Samples.Sample
;
import
com.google.android.exoplayer.demo.full.FullPlayerActivity
;
import
com.google.android.exoplayer.demo.simple.SimplePlayerActivity
;
import
com.google.android.exoplayer.util.Util
;
import
android.app.Activity
;
import
android.content.Context
;
...
...
@@ -33,7 +32,6 @@ import android.widget.AdapterView.OnItemClickListener;
import
android.widget.ArrayAdapter
;
import
android.widget.ListView
;
import
android.widget.TextView
;
import
android.widget.Toast
;
/**
* An activity for selecting from a number of samples.
...
...
@@ -78,11 +76,6 @@ public class SampleChooserActivity extends Activity {
}
private
void
onSampleSelected
(
Sample
sample
)
{
if
(
Util
.
SDK_INT
<
18
&&
sample
.
isEncypted
)
{
Toast
.
makeText
(
getApplicationContext
(),
R
.
string
.
drm_not_supported
,
Toast
.
LENGTH_SHORT
)
.
show
();
return
;
}
Class
<?>
playerActivityClass
=
sample
.
fullPlayer
?
FullPlayerActivity
.
class
:
SimplePlayerActivity
.
class
;
Intent
mpdIntent
=
new
Intent
(
this
,
playerActivityClass
)
...
...
demo/src/main/java/com/google/android/exoplayer/demo/Samples.java
View file @
4c29eb58
...
...
@@ -26,16 +26,13 @@ package com.google.android.exoplayer.demo;
public
final
String
contentId
;
public
final
String
uri
;
public
final
int
type
;
public
final
boolean
isEncypted
;
public
final
boolean
fullPlayer
;
public
Sample
(
String
name
,
String
contentId
,
String
uri
,
int
type
,
boolean
isEncrypted
,
boolean
fullPlayer
)
{
public
Sample
(
String
name
,
String
contentId
,
String
uri
,
int
type
,
boolean
fullPlayer
)
{
this
.
name
=
name
;
this
.
contentId
=
contentId
;
this
.
uri
=
uri
;
this
.
type
=
type
;
this
.
isEncypted
=
isEncrypted
;
this
.
fullPlayer
=
fullPlayer
;
}
...
...
@@ -46,22 +43,20 @@ package com.google.android.exoplayer.demo;
"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&"
+
"ipbits=0&expire=19000000000&signature=255F6B3C07C753C88708C07EA31B7A1A10703C8D."
+
"2D6A28B21F921D0B245CDCF36F7EB54A2B5ABFC2&key=ik0"
,
DemoUtil
.
TYPE_DASH
,
false
,
false
),
+
"2D6A28B21F921D0B245CDCF36F7EB54A2B5ABFC2&key=ik0"
,
DemoUtil
.
TYPE_DASH
,
false
),
new
Sample
(
"Google Play (DASH)"
,
"3aa39fa2cc27967f"
,
"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&"
+
"expire=19000000000&signature=7181C59D0252B285D593E1B61D985D5B7C98DE2A."
+
"5B445837F55A40E0F28AACAA047982E372D177E2&key=ik0"
,
DemoUtil
.
TYPE_DASH
,
false
,
false
),
+
"5B445837F55A40E0F28AACAA047982E372D177E2&key=ik0"
,
DemoUtil
.
TYPE_DASH
,
false
),
new
Sample
(
"Super speed (SmoothStreaming)"
,
"uid:ss:superspeed"
,
"http://playready.directtaps.net/smoothstreaming/SSWSS720H264/SuperSpeedway_720.ism"
,
DemoUtil
.
TYPE_SS
,
false
,
false
),
DemoUtil
.
TYPE_SS
,
false
),
new
Sample
(
"Apple master playlist (HLS)"
,
"uid:hls:applemaster"
,
"https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/"
+
"bipbop_4x3_variant.m3u8"
,
DemoUtil
.
TYPE_HLS
,
false
,
false
),
+
"bipbop_4x3_variant.m3u8"
,
DemoUtil
.
TYPE_HLS
,
false
),
new
Sample
(
"Dizzy (Misc)"
,
"uid:misc:dizzy"
,
"http://html5demos.com/assets/dizzy.mp4"
,
DemoUtil
.
TYPE_OTHER
,
false
,
false
),
"http://html5demos.com/assets/dizzy.mp4"
,
DemoUtil
.
TYPE_OTHER
,
false
),
};
public
static
final
Sample
[]
YOUTUBE_DASH_MP4
=
new
Sample
[]
{
...
...
@@ -69,14 +64,12 @@ package com.google.android.exoplayer.demo;
"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&"
+
"ipbits=0&expire=19000000000&signature=255F6B3C07C753C88708C07EA31B7A1A10703C8D."
+
"2D6A28B21F921D0B245CDCF36F7EB54A2B5ABFC2&key=ik0"
,
DemoUtil
.
TYPE_DASH
,
false
,
true
),
+
"2D6A28B21F921D0B245CDCF36F7EB54A2B5ABFC2&key=ik0"
,
DemoUtil
.
TYPE_DASH
,
true
),
new
Sample
(
"Google Play"
,
"3aa39fa2cc27967f"
,
"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&"
+
"expire=19000000000&signature=7181C59D0252B285D593E1B61D985D5B7C98DE2A."
+
"5B445837F55A40E0F28AACAA047982E372D177E2&key=ik0"
,
DemoUtil
.
TYPE_DASH
,
false
,
true
),
+
"5B445837F55A40E0F28AACAA047982E372D177E2&key=ik0"
,
DemoUtil
.
TYPE_DASH
,
true
),
};
public
static
final
Sample
[]
YOUTUBE_DASH_WEBM
=
new
Sample
[]
{
...
...
@@ -84,21 +77,21 @@ package com.google.android.exoplayer.demo;
"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&"
+
"expire=19000000000&signature=A3EC7EE53ABE601B357F7CAB8B54AD0702CA85A7."
+
"446E9C38E47E3EDAF39E0163C390FF83A7944918&key=ik0"
,
DemoUtil
.
TYPE_DASH
,
false
,
true
),
+
"446E9C38E47E3EDAF39E0163C390FF83A7944918&key=ik0"
,
DemoUtil
.
TYPE_DASH
,
true
),
new
Sample
(
"Google Play"
,
"3aa39fa2cc27967f"
,
"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&"
+
"expire=19000000000&signature=B752B262C6D7262EC4E4EB67901E5D8F7058A81D."
+
"C0358CE1E335417D9A8D88FF192F0D5D8F6DA1B6&key=ik0"
,
DemoUtil
.
TYPE_DASH
,
false
,
true
),
+
"C0358CE1E335417D9A8D88FF192F0D5D8F6DA1B6&key=ik0"
,
DemoUtil
.
TYPE_DASH
,
true
),
};
public
static
final
Sample
[]
SMOOTHSTREAMING
=
new
Sample
[]
{
new
Sample
(
"Super speed"
,
"uid:ss:superspeed"
,
"http://playready.directtaps.net/smoothstreaming/SSWSS720H264/SuperSpeedway_720.ism"
,
DemoUtil
.
TYPE_SS
,
false
,
true
),
DemoUtil
.
TYPE_SS
,
true
),
new
Sample
(
"Super speed (PlayReady)"
,
"uid:ss:pr:superspeed"
,
"http://playready.directtaps.net/smoothstreaming/SSWSS720H264PR/SuperSpeedway_720.ism"
,
DemoUtil
.
TYPE_SS
,
true
,
true
),
DemoUtil
.
TYPE_SS
,
true
),
};
public
static
final
Sample
[]
WIDEVINE_GTS
=
new
Sample
[]
{
...
...
@@ -106,54 +99,54 @@ package com.google.android.exoplayer.demo;
"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"
+
"&expire=19000000000&signature=41EA40A027A125A16292E0A5E3277A3B5FA9B938."
+
"0BB075C396FFDDC97E526E8F77DC26FF9667D0D6&key=ik0"
,
DemoUtil
.
TYPE_DASH
,
true
,
true
),
+
"0BB075C396FFDDC97E526E8F77DC26FF9667D0D6&key=ik0"
,
DemoUtil
.
TYPE_DASH
,
true
),
new
Sample
(
"WV: HDCP not required"
,
"48fcc369939ac96c"
,
"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"
+
"&expire=19000000000&signature=315911BDCEED0FB0C763455BDCC97449DAAFA9E8."
+
"5B41E2EB411F797097A359D6671D2CDE26272373&key=ik0"
,
DemoUtil
.
TYPE_DASH
,
true
,
true
),
+
"5B41E2EB411F797097A359D6671D2CDE26272373&key=ik0"
,
DemoUtil
.
TYPE_DASH
,
true
),
new
Sample
(
"WV: HDCP required"
,
"e06c39f1151da3df"
,
"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"
+
"&expire=19000000000&signature=A47A1E13E7243BD567601A75F79B34644D0DC592."
+
"B09589A34FA23527EFC1552907754BB8033870BD&key=ik0"
,
DemoUtil
.
TYPE_DASH
,
true
,
true
),
+
"B09589A34FA23527EFC1552907754BB8033870BD&key=ik0"
,
DemoUtil
.
TYPE_DASH
,
true
),
new
Sample
(
"WV: Secure video path required"
,
"0894c7c8719b28a0"
,
"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"
+
"&expire=19000000000&signature=2847EE498970F6B45176766CD2802FEB4D4CB7B2."
+
"A1CA51EC40A1C1039BA800C41500DD448C03EEDA&key=ik0"
,
DemoUtil
.
TYPE_DASH
,
true
,
true
),
+
"A1CA51EC40A1C1039BA800C41500DD448C03EEDA&key=ik0"
,
DemoUtil
.
TYPE_DASH
,
true
),
new
Sample
(
"WV: HDCP + secure video path required"
,
"efd045b1eb61888a"
,
"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"
+
"&expire=19000000000&signature=61611F115EEEC7BADE5536827343FFFE2D83D14F."
+
"2FDF4BFA502FB5865C5C86401314BDDEA4799BD0&key=ik0"
,
DemoUtil
.
TYPE_DASH
,
true
,
true
),
+
"2FDF4BFA502FB5865C5C86401314BDDEA4799BD0&key=ik0"
,
DemoUtil
.
TYPE_DASH
,
true
),
new
Sample
(
"WV: 30s license duration"
,
"f9a34cab7b05881a"
,
"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"
+
"&expire=19000000000&signature=88DC53943385CED8CF9F37ADD9E9843E3BF621E6."
+
"22727BB612D24AA4FACE4EF62726F9461A9BF57A&key=ik0"
,
DemoUtil
.
TYPE_DASH
,
true
,
true
),
+
"22727BB612D24AA4FACE4EF62726F9461A9BF57A&key=ik0"
,
DemoUtil
.
TYPE_DASH
,
true
),
};
public
static
final
Sample
[]
HLS
=
new
Sample
[]
{
new
Sample
(
"Apple master playlist"
,
"uid:hls:applemaster"
,
"https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/"
+
"bipbop_4x3_variant.m3u8"
,
DemoUtil
.
TYPE_HLS
,
false
,
true
),
+
"bipbop_4x3_variant.m3u8"
,
DemoUtil
.
TYPE_HLS
,
true
),
new
Sample
(
"Apple master playlist advanced"
,
"uid:hls:applemasteradvanced"
,
"https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_16x9/"
+
"bipbop_16x9_variant.m3u8"
,
DemoUtil
.
TYPE_HLS
,
false
,
true
),
+
"bipbop_16x9_variant.m3u8"
,
DemoUtil
.
TYPE_HLS
,
true
),
new
Sample
(
"Apple single media playlist"
,
"uid:hls:applesinglemedia"
,
"https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear1/"
+
"prog_index.m3u8"
,
DemoUtil
.
TYPE_HLS
,
false
,
true
),
+
"prog_index.m3u8"
,
DemoUtil
.
TYPE_HLS
,
true
),
};
public
static
final
Sample
[]
MISC
=
new
Sample
[]
{
new
Sample
(
"Dizzy"
,
"uid:misc:dizzy"
,
"http://html5demos.com/assets/dizzy.mp4"
,
DemoUtil
.
TYPE_OTHER
,
false
,
true
),
DemoUtil
.
TYPE_OTHER
,
true
),
new
Sample
(
"Dizzy (https->http redirect)"
,
"uid:misc:dizzy2"
,
"https://goo.gl/MtUDEj"
,
DemoUtil
.
TYPE_OTHER
,
false
,
true
),
DemoUtil
.
TYPE_OTHER
,
true
),
new
Sample
(
"Apple AAC 10s"
,
"uid:misc:appleaacseg"
,
"https://devimages.apple.com.edgekey.net/"
+
"streaming/examples/bipbop_4x3/gear0/fileSequence0.aac"
,
DemoUtil
.
TYPE_OTHER
,
false
,
true
),
DemoUtil
.
TYPE_OTHER
,
true
),
};
private
Samples
()
{}
...
...
demo/src/main/java/com/google/android/exoplayer/demo/full/FullPlayerActivity.java
View file @
4c29eb58
...
...
@@ -17,6 +17,8 @@ package com.google.android.exoplayer.demo.full;
import
com.google.android.exoplayer.ExoPlayer
;
import
com.google.android.exoplayer.VideoSurfaceView
;
import
com.google.android.exoplayer.audio.AudioCapabilities
;
import
com.google.android.exoplayer.audio.AudioCapabilitiesReceiver
;
import
com.google.android.exoplayer.demo.DemoUtil
;
import
com.google.android.exoplayer.demo.R
;
import
com.google.android.exoplayer.demo.full.player.DashRendererBuilder
;
...
...
@@ -25,6 +27,7 @@ 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.HlsRendererBuilder
;
import
com.google.android.exoplayer.demo.full.player.SmoothStreamingRendererBuilder
;
import
com.google.android.exoplayer.demo.full.player.UnsupportedDrmException
;
import
com.google.android.exoplayer.metadata.TxxxMetadata
;
import
com.google.android.exoplayer.text.CaptionStyleCompat
;
import
com.google.android.exoplayer.text.SubtitleView
;
...
...
@@ -55,6 +58,7 @@ import android.widget.MediaController;
import
android.widget.PopupMenu
;
import
android.widget.PopupMenu.OnMenuItemClickListener
;
import
android.widget.TextView
;
import
android.widget.Toast
;
import
java.util.Map
;
...
...
@@ -62,7 +66,8 @@ import java.util.Map;
* An activity that plays media using {@link DemoPlayer}.
*/
public
class
FullPlayerActivity
extends
Activity
implements
SurfaceHolder
.
Callback
,
OnClickListener
,
DemoPlayer
.
Listener
,
DemoPlayer
.
TextListener
,
DemoPlayer
.
Id3MetadataListener
{
DemoPlayer
.
Listener
,
DemoPlayer
.
TextListener
,
DemoPlayer
.
Id3MetadataListener
,
AudioCapabilitiesReceiver
.
Listener
{
private
static
final
String
TAG
=
"FullPlayerActivity"
;
...
...
@@ -94,6 +99,9 @@ public class FullPlayerActivity extends Activity implements SurfaceHolder.Callba
private
int
contentType
;
private
String
contentId
;
private
AudioCapabilitiesReceiver
audioCapabilitiesReceiver
;
private
AudioCapabilities
audioCapabilities
;
// Activity lifecycle
@Override
...
...
@@ -117,6 +125,8 @@ public class FullPlayerActivity extends Activity implements SurfaceHolder.Callba
}
});
audioCapabilitiesReceiver
=
new
AudioCapabilitiesReceiver
(
getApplicationContext
(),
this
);
shutterView
=
findViewById
(
R
.
id
.
shutter
);
debugRootView
=
findViewById
(
R
.
id
.
controls_root
);
...
...
@@ -142,7 +152,9 @@ public class FullPlayerActivity extends Activity implements SurfaceHolder.Callba
public
void
onResume
()
{
super
.
onResume
();
configureSubtitleView
();
preparePlayer
();
// The player will be prepared on receiving audio capabilities.
audioCapabilitiesReceiver
.
register
();
}
@Override
...
...
@@ -153,6 +165,8 @@ public class FullPlayerActivity extends Activity implements SurfaceHolder.Callba
}
else
{
player
.
blockingClearSurface
();
}
audioCapabilitiesReceiver
.
unregister
();
}
@Override
...
...
@@ -171,6 +185,17 @@ public class FullPlayerActivity extends Activity implements SurfaceHolder.Callba
}
}
// AudioCapabilitiesReceiver.Listener methods
@Override
public
void
onAudioCapabilitiesChanged
(
AudioCapabilities
audioCapabilities
)
{
this
.
audioCapabilities
=
audioCapabilities
;
releasePlayer
();
autoPlay
=
true
;
preparePlayer
();
}
// Internal methods
private
RendererBuilder
getRendererBuilder
()
{
...
...
@@ -181,7 +206,7 @@ public class FullPlayerActivity extends Activity implements SurfaceHolder.Callba
new
SmoothStreamingTestMediaDrmCallback
(),
debugTextView
);
case
DemoUtil
.
TYPE_DASH
:
return
new
DashRendererBuilder
(
userAgent
,
contentUri
.
toString
(),
contentId
,
new
WidevineTestMediaDrmCallback
(
contentId
),
debugTextView
);
new
WidevineTestMediaDrmCallback
(
contentId
),
debugTextView
,
audioCapabilities
);
case
DemoUtil
.
TYPE_HLS
:
return
new
HlsRendererBuilder
(
userAgent
,
contentUri
.
toString
(),
contentId
);
default
:
...
...
@@ -266,6 +291,16 @@ public class FullPlayerActivity extends Activity implements SurfaceHolder.Callba
@Override
public
void
onError
(
Exception
e
)
{
if
(
e
instanceof
UnsupportedDrmException
)
{
// Special case DRM failures.
UnsupportedDrmException
unsupportedDrmException
=
(
UnsupportedDrmException
)
e
;
int
stringId
=
unsupportedDrmException
.
reason
==
UnsupportedDrmException
.
REASON_NO_DRM
?
R
.
string
.
drm_error_not_supported
:
unsupportedDrmException
.
reason
==
UnsupportedDrmException
.
REASON_UNSUPPORTED_SCHEME
?
R
.
string
.
drm_error_unsupported_scheme
:
R
.
string
.
drm_error_unknown
;
Toast
.
makeText
(
getApplicationContext
(),
stringId
,
Toast
.
LENGTH_LONG
).
show
();
}
playerNeedsPrepare
=
true
;
updateButtonVisibilities
();
showControls
();
...
...
demo/src/main/java/com/google/android/exoplayer/demo/full/player/DashRendererBuilder.java
View file @
4c29eb58
...
...
@@ -15,6 +15,7 @@
*/
package
com
.
google
.
android
.
exoplayer
.
demo
.
full
.
player
;
import
com.google.android.exoplayer.Ac3PassthroughAudioTrackRenderer
;
import
com.google.android.exoplayer.DefaultLoadControl
;
import
com.google.android.exoplayer.LoadControl
;
import
com.google.android.exoplayer.MediaCodecAudioTrackRenderer
;
...
...
@@ -22,6 +23,7 @@ import com.google.android.exoplayer.MediaCodecUtil;
import
com.google.android.exoplayer.MediaCodecVideoTrackRenderer
;
import
com.google.android.exoplayer.SampleSource
;
import
com.google.android.exoplayer.TrackRenderer
;
import
com.google.android.exoplayer.audio.AudioCapabilities
;
import
com.google.android.exoplayer.chunk.ChunkSampleSource
;
import
com.google.android.exoplayer.chunk.ChunkSource
;
import
com.google.android.exoplayer.chunk.Format
;
...
...
@@ -41,6 +43,7 @@ import com.google.android.exoplayer.drm.DrmSessionManager;
import
com.google.android.exoplayer.drm.MediaDrmCallback
;
import
com.google.android.exoplayer.drm.StreamingDrmSessionManager
;
import
com.google.android.exoplayer.text.TextTrackRenderer
;
import
com.google.android.exoplayer.text.ttml.TtmlParser
;
import
com.google.android.exoplayer.text.webvtt.WebvttParser
;
import
com.google.android.exoplayer.upstream.BufferPool
;
import
com.google.android.exoplayer.upstream.DataSource
;
...
...
@@ -83,18 +86,20 @@ public class DashRendererBuilder implements RendererBuilder,
private
final
String
contentId
;
private
final
MediaDrmCallback
drmCallback
;
private
final
TextView
debugTextView
;
private
final
AudioCapabilities
audioCapabilities
;
private
DemoPlayer
player
;
private
RendererBuilderCallback
callback
;
private
ManifestFetcher
<
MediaPresentationDescription
>
manifestFetcher
;
public
DashRendererBuilder
(
String
userAgent
,
String
url
,
String
contentId
,
MediaDrmCallback
drmCallback
,
TextView
debugTextView
)
{
MediaDrmCallback
drmCallback
,
TextView
debugTextView
,
AudioCapabilities
audioCapabilities
)
{
this
.
userAgent
=
userAgent
;
this
.
url
=
url
;
this
.
contentId
=
contentId
;
this
.
drmCallback
=
drmCallback
;
this
.
debugTextView
=
debugTextView
;
this
.
audioCapabilities
=
audioCapabilities
;
}
@Override
...
...
@@ -119,17 +124,33 @@ public class DashRendererBuilder implements RendererBuilder,
LoadControl
loadControl
=
new
DefaultLoadControl
(
new
BufferPool
(
BUFFER_SEGMENT_SIZE
));
DefaultBandwidthMeter
bandwidthMeter
=
new
DefaultBandwidthMeter
(
mainHandler
,
player
);
boolean
hasContentProtection
=
false
;
int
videoAdaptationSetIndex
=
period
.
getAdaptationSetIndex
(
AdaptationSet
.
TYPE_VIDEO
);
AdaptationSet
videoAdaptationSet
=
period
.
adaptationSets
.
get
(
videoAdaptationSetIndex
);
int
audioAdaptationSetIndex
=
period
.
getAdaptationSetIndex
(
AdaptationSet
.
TYPE_AUDIO
);
AdaptationSet
videoAdaptationSet
=
null
;
AdaptationSet
audioAdaptationSet
=
null
;
if
(
videoAdaptationSetIndex
!=
-
1
)
{
videoAdaptationSet
=
period
.
adaptationSets
.
get
(
videoAdaptationSetIndex
);
hasContentProtection
|=
videoAdaptationSet
.
hasContentProtection
();
}
if
(
audioAdaptationSetIndex
!=
-
1
)
{
audioAdaptationSet
=
period
.
adaptationSets
.
get
(
audioAdaptationSetIndex
);
hasContentProtection
|=
audioAdaptationSet
.
hasContentProtection
();
}
// Fail if we have neither video or audio.
if
(
videoAdaptationSet
==
null
&&
audioAdaptationSet
==
null
)
{
callback
.
onRenderersError
(
new
IllegalStateException
(
"No video or audio adaptation sets"
));
return
;
}
// Check drm support if necessary.
boolean
hasContentProtection
=
videoAdaptationSet
.
hasContentProtection
();
boolean
filterHdContent
=
false
;
DrmSessionManager
drmSessionManager
=
null
;
if
(
hasContentProtection
)
{
if
(
Util
.
SDK_INT
<
18
)
{
callback
.
onRenderersError
(
new
UnsupportedOperationException
(
"Protected content not supported on API level "
+
Util
.
SDK_INT
));
callback
.
onRenderersError
(
new
UnsupportedDrmException
(
UnsupportedDrmException
.
REASON_NO_DRM
));
return
;
}
try
{
...
...
@@ -137,17 +158,23 @@ public class DashRendererBuilder implements RendererBuilder,
V18Compat
.
getDrmSessionManagerData
(
player
,
drmCallback
);
drmSessionManager
=
drmSessionManagerData
.
first
;
// HD streams require L1 security.
filterHdContent
=
!
drmSessionManagerData
.
second
;
filterHdContent
=
videoAdaptationSet
!=
null
&&
videoAdaptationSet
.
hasContentProtection
()
&&
!
drmSessionManagerData
.
second
;
}
catch
(
UnsupportedSchemeException
e
)
{
callback
.
onRenderersError
(
new
UnsupportedDrmException
(
UnsupportedDrmException
.
REASON_UNSUPPORTED_SCHEME
,
e
));
}
catch
(
Exception
e
)
{
callback
.
onRenderersError
(
e
);
callback
.
onRenderersError
(
new
UnsupportedDrmException
(
UnsupportedDrmException
.
REASON_UNKNOWN
,
e
));
return
;
}
}
// Determine which video representations we should use for playback.
ArrayList
<
Integer
>
videoRepresentationIndexList
=
new
ArrayList
<
Integer
>();
if
(
videoAdaptationSet
!=
null
)
{
int
maxDecodableFrameSize
=
MediaCodecUtil
.
maxH264DecodableFrameSize
();
List
<
Representation
>
videoRepresentations
=
videoAdaptationSet
.
representations
;
ArrayList
<
Integer
>
videoRepresentationIndexList
=
new
ArrayList
<
Integer
>();
for
(
int
i
=
0
;
i
<
videoRepresentations
.
size
();
i
++)
{
Format
format
=
videoRepresentations
.
get
(
i
).
format
;
if
(
filterHdContent
&&
(
format
.
width
>=
1280
||
format
.
height
>=
720
))
{
...
...
@@ -161,6 +188,7 @@ public class DashRendererBuilder implements RendererBuilder,
videoRepresentationIndexList
.
add
(
i
);
}
}
}
// Build the video renderer.
final
MediaCodecVideoTrackRenderer
videoRenderer
;
...
...
@@ -184,12 +212,12 @@ public class DashRendererBuilder implements RendererBuilder,
}
// 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
();
boolean
haveAc3Tracks
=
false
;
List
<
ChunkSource
>
audioChunkSourceList
=
new
ArrayList
<
ChunkSource
>();
List
<
String
>
audioTrackNameList
=
new
ArrayList
<
String
>();
if
(
audioAdaptationSet
!=
null
)
{
DataSource
audioDataSource
=
new
UriDataSource
(
userAgent
,
bandwidthMeter
);
FormatEvaluator
audioEvaluator
=
new
FormatEvaluator
.
FixedEvaluator
();
List
<
Representation
>
audioRepresentations
=
audioAdaptationSet
.
representations
;
for
(
int
i
=
0
;
i
<
audioRepresentations
.
size
();
i
++)
{
Format
format
=
audioRepresentations
.
get
(
i
).
format
;
...
...
@@ -197,6 +225,20 @@ public class DashRendererBuilder implements RendererBuilder,
format
.
audioSamplingRate
+
"Hz)"
);
audioChunkSourceList
.
add
(
new
DashChunkSource
(
manifestFetcher
,
audioAdaptationSetIndex
,
new
int
[]
{
i
},
audioDataSource
,
audioEvaluator
,
LIVE_EDGE_LATENCY_MS
));
haveAc3Tracks
|=
format
.
mimeType
.
equals
(
MimeTypes
.
AUDIO_AC3
)
||
format
.
mimeType
.
equals
(
MimeTypes
.
AUDIO_EC3
);
}
// Filter out non-AC-3 tracks if there is an AC-3 track, to avoid having to switch renderers.
if
(
haveAc3Tracks
)
{
for
(
int
i
=
audioRepresentations
.
size
()
-
1
;
i
>=
0
;
i
--)
{
Format
format
=
audioRepresentations
.
get
(
i
).
format
;
if
(!
format
.
mimeType
.
equals
(
MimeTypes
.
AUDIO_AC3
)
&&
!
format
.
mimeType
.
equals
(
MimeTypes
.
AUDIO_EC3
))
{
audioTrackNameList
.
remove
(
i
);
audioChunkSourceList
.
remove
(
i
);
}
}
}
}
// Build the audio renderer.
...
...
@@ -214,9 +256,17 @@ public class DashRendererBuilder implements RendererBuilder,
SampleSource
audioSampleSource
=
new
ChunkSampleSource
(
audioChunkSource
,
loadControl
,
AUDIO_BUFFER_SEGMENTS
*
BUFFER_SEGMENT_SIZE
,
true
,
mainHandler
,
player
,
DemoPlayer
.
TYPE_AUDIO
);
// TODO: There needs to be some logic to filter out non-AC3 tracks when selecting to use AC3.
boolean
useAc3Passthrough
=
haveAc3Tracks
&&
audioCapabilities
!=
null
&&
(
audioCapabilities
.
supportsAc3
()
||
audioCapabilities
.
supportsEAc3
());
if
(
useAc3Passthrough
)
{
audioRenderer
=
new
Ac3PassthroughAudioTrackRenderer
(
audioSampleSource
,
mainHandler
,
player
);
}
else
{
audioRenderer
=
new
MediaCodecAudioTrackRenderer
(
audioSampleSource
,
drmSessionManager
,
true
,
mainHandler
,
player
);
}
}
// Build the text chunk sources.
DataSource
textDataSource
=
new
UriDataSource
(
userAgent
,
bandwidthMeter
);
...
...
@@ -251,8 +301,8 @@ public class DashRendererBuilder implements RendererBuilder,
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
.
getLoop
er
());
textRenderer
=
new
TextTrackRenderer
(
textSampleSource
,
player
,
mainHandler
.
getLooper
()
,
new
TtmlParser
(),
new
WebvttPars
er
());
}
// Invoke the callback.
...
...
demo/src/main/java/com/google/android/exoplayer/demo/full/player/DemoPlayer.java
View file @
4c29eb58
...
...
@@ -15,6 +15,7 @@
*/
package
com
.
google
.
android
.
exoplayer
.
demo
.
full
.
player
;
import
com.google.android.exoplayer.Ac3PassthroughAudioTrackRenderer
;
import
com.google.android.exoplayer.DummyTrackRenderer
;
import
com.google.android.exoplayer.ExoPlaybackException
;
import
com.google.android.exoplayer.ExoPlayer
;
...
...
@@ -49,8 +50,8 @@ import java.util.concurrent.CopyOnWriteArrayList;
*/
public
class
DemoPlayer
implements
ExoPlayer
.
Listener
,
ChunkSampleSource
.
EventListener
,
DefaultBandwidthMeter
.
EventListener
,
MediaCodecVideoTrackRenderer
.
EventListener
,
MediaCodecAudioTrackRenderer
.
EventListener
,
TextTrackRenderer
.
TextRender
er
,
StreamingDrmSessionManager
.
EventListener
{
MediaCodecAudioTrackRenderer
.
EventListener
,
Ac3PassthroughAudioTrackRenderer
.
EventListen
er
,
TextTrackRenderer
.
TextRenderer
,
StreamingDrmSessionManager
.
EventListener
{
/**
* Builds renderers for the player.
...
...
demo/src/main/java/com/google/android/exoplayer/demo/full/player/SmoothStreamingRendererBuilder.java
View file @
4c29eb58
...
...
@@ -111,15 +111,19 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
DrmSessionManager
drmSessionManager
=
null
;
if
(
manifest
.
protectionElement
!=
null
)
{
if
(
Util
.
SDK_INT
<
18
)
{
callback
.
onRenderersError
(
new
UnsupportedOperationException
(
"Protected content not supported on API level "
+
Util
.
SDK_INT
));
callback
.
onRenderersError
(
new
UnsupportedDrmException
(
UnsupportedDrmException
.
REASON_NO_DRM
));
return
;
}
try
{
drmSessionManager
=
V18Compat
.
getDrmSessionManager
(
manifest
.
protectionElement
.
uuid
,
player
,
drmCallback
);
}
catch
(
UnsupportedSchemeException
e
)
{
callback
.
onRenderersError
(
new
UnsupportedDrmException
(
UnsupportedDrmException
.
REASON_UNSUPPORTED_SCHEME
,
e
));
}
catch
(
Exception
e
)
{
callback
.
onRenderersError
(
e
);
callback
.
onRenderersError
(
new
UnsupportedDrmException
(
UnsupportedDrmException
.
REASON_UNKNOWN
,
e
));
return
;
}
}
...
...
@@ -149,9 +153,15 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
}
}
}
int
[]
videoTrackIndices
=
Util
.
toArray
(
videoTrackIndexList
);
// Build the video renderer.
final
MediaCodecVideoTrackRenderer
videoRenderer
;
final
TrackRenderer
debugRenderer
;
if
(
videoTrackIndexList
.
isEmpty
())
{
videoRenderer
=
null
;
debugRenderer
=
null
;
}
else
{
int
[]
videoTrackIndices
=
Util
.
toArray
(
videoTrackIndexList
);
DataSource
videoDataSource
=
new
UriDataSource
(
userAgent
,
bandwidthMeter
);
ChunkSource
videoChunkSource
=
new
SmoothStreamingChunkSource
(
manifestFetcher
,
videoStreamElementIndex
,
videoTrackIndices
,
videoDataSource
,
...
...
@@ -159,9 +169,12 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
ChunkSampleSource
videoSampleSource
=
new
ChunkSampleSource
(
videoChunkSource
,
loadControl
,
VIDEO_BUFFER_SEGMENTS
*
BUFFER_SEGMENT_SIZE
,
true
,
mainHandler
,
player
,
DemoPlayer
.
TYPE_VIDEO
);
MediaCodecVideoTrackRenderer
videoRenderer
=
new
MediaCodecVideoTrackRenderer
(
videoSampleSource
,
drmSessionManager
,
true
,
MediaCodec
.
VIDEO_SCALING_MODE_SCALE_TO_FIT
,
5000
,
null
,
mainHandler
,
player
,
50
);
videoRenderer
=
new
MediaCodecVideoTrackRenderer
(
videoSampleSource
,
drmSessionManager
,
true
,
MediaCodec
.
VIDEO_SCALING_MODE_SCALE_TO_FIT
,
5000
,
null
,
mainHandler
,
player
,
50
);
debugRenderer
=
debugTextView
!=
null
?
new
DebugTrackRenderer
(
debugTextView
,
videoRenderer
,
videoSampleSource
)
:
null
;
}
// Build the audio renderer.
final
String
[]
audioTrackNames
;
...
...
@@ -220,15 +233,10 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
ChunkSampleSource
ttmlSampleSource
=
new
ChunkSampleSource
(
textChunkSource
,
loadControl
,
TEXT_BUFFER_SEGMENTS
*
BUFFER_SEGMENT_SIZE
,
true
,
mainHandler
,
player
,
DemoPlayer
.
TYPE_TEXT
);
textRenderer
=
new
TextTrackRenderer
(
ttmlSampleSource
,
new
TtmlParser
(),
player
,
mainHandler
.
getLoop
er
());
textRenderer
=
new
TextTrackRenderer
(
ttmlSampleSource
,
player
,
mainHandler
.
getLooper
()
,
new
TtmlPars
er
());
}
// Build the debug renderer.
TrackRenderer
debugRenderer
=
debugTextView
!=
null
?
new
DebugTrackRenderer
(
debugTextView
,
videoRenderer
,
videoSampleSource
)
:
null
;
// Invoke the callback.
String
[][]
trackNames
=
new
String
[
DemoPlayer
.
RENDERER_COUNT
][];
trackNames
[
DemoPlayer
.
TYPE_AUDIO
]
=
audioTrackNames
;
...
...
demo/src/main/java/com/google/android/exoplayer/demo/full/player/UnsupportedDrmException.java
0 → 100644
View file @
4c29eb58
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
com
.
google
.
android
.
exoplayer
.
demo
.
full
.
player
;
/**
* Exception thrown when the required level of DRM is not supported.
*/
public
final
class
UnsupportedDrmException
extends
Exception
{
public
static
final
int
REASON_NO_DRM
=
0
;
public
static
final
int
REASON_UNSUPPORTED_SCHEME
=
1
;
public
static
final
int
REASON_UNKNOWN
=
2
;
public
final
int
reason
;
public
UnsupportedDrmException
(
int
reason
)
{
this
.
reason
=
reason
;
}
public
UnsupportedDrmException
(
int
reason
,
Exception
cause
)
{
super
(
cause
);
this
.
reason
=
reason
;
}
}
demo/src/main/res/values/strings.xml
View file @
4c29eb58
...
...
@@ -39,7 +39,11 @@
<string
name=
"on"
>
[on]
</string>
<string
name=
"drm_not_supported"
>
Protected content not supported on API levels below 18
</string>
<string
name=
"drm_error_not_supported"
>
Protected content not supported on API levels below 18
</string>
<string
name=
"drm_error_unsupported_scheme"
>
This device does not support the required DRM scheme
</string>
<string
name=
"drm_error_unknown"
>
An unknown DRM error occurred
</string>
<string
name=
"failed"
>
Playback failed
</string>
...
...
library/src/main/java/com/google/android/exoplayer/Ac3PassthroughAudioTrackRenderer.java
0 → 100644
View file @
4c29eb58
This diff is collapsed.
Click to expand it.
library/src/main/java/com/google/android/exoplayer/MediaFormat.java
View file @
4c29eb58
...
...
@@ -88,13 +88,20 @@ public class MediaFormat {
}
public
static
MediaFormat
createId3Format
()
{
return
new
MediaFormat
(
MimeTypes
.
APPLICATION_ID3
,
NO_VALUE
,
NO_VALUE
,
NO_VALUE
,
NO_VALUE
,
NO_VALUE
,
NO_VALUE
,
NO_VALUE
,
null
);
return
createFormatForMimeType
(
MimeTypes
.
APPLICATION_ID3
);
}
public
static
MediaFormat
createEia608Format
()
{
return
new
MediaFormat
(
MimeTypes
.
APPLICATION_EIA608
,
NO_VALUE
,
NO_VALUE
,
NO_VALUE
,
NO_VALUE
,
NO_VALUE
,
NO_VALUE
,
NO_VALUE
,
null
);
return
createFormatForMimeType
(
MimeTypes
.
APPLICATION_EIA608
);
}
public
static
MediaFormat
createTtmlFormat
()
{
return
createFormatForMimeType
(
MimeTypes
.
APPLICATION_TTML
);
}
public
static
MediaFormat
createFormatForMimeType
(
String
mimeType
)
{
return
new
MediaFormat
(
mimeType
,
NO_VALUE
,
NO_VALUE
,
NO_VALUE
,
NO_VALUE
,
NO_VALUE
,
NO_VALUE
,
NO_VALUE
,
null
);
}
@TargetApi
(
16
)
...
...
library/src/main/java/com/google/android/exoplayer/TrackRenderer.java
View file @
4c29eb58
...
...
@@ -18,8 +18,6 @@ package com.google.android.exoplayer;
import
com.google.android.exoplayer.ExoPlayer.ExoPlayerComponent
;
import
com.google.android.exoplayer.util.Assertions
;
import
android.os.SystemClock
;
/**
* Renders a single component of media.
*
...
...
@@ -293,8 +291,8 @@ public abstract class TrackRenderer implements ExoPlayerComponent {
*
* @param positionUs The current media time in microseconds, measured at the start of the
* current iteration of the rendering loop.
* @param elapsedRealtimeUs {@link
SystemClock#elapsedRealtime()} in microseconds, measured at
* the start of the current iteration of the rendering loop.
* @param elapsedRealtimeUs {@link
android.os.SystemClock#elapsedRealtime()} in microseconds,
*
measured at
the start of the current iteration of the rendering loop.
* @throws ExoPlaybackException If an error occurs.
*/
protected
abstract
void
doSomeWork
(
long
positionUs
,
long
elapsedRealtimeUs
)
...
...
library/src/main/java/com/google/android/exoplayer/chunk/ChunkSampleSource.java
View file @
4c29eb58
...
...
@@ -272,16 +272,23 @@ public class ChunkSampleSource implements SampleSource, Loader.Callback {
downstreamPositionUs
=
positionUs
;
chunkSource
.
continueBuffering
(
positionUs
);
updateLoadControl
();
boolean
haveSamples
=
false
;
if
(
isPendingReset
()
||
mediaChunks
.
isEmpty
())
{
return
false
;
// No sample available.
}
else
if
(
mediaChunks
.
getFirst
().
sampleAvailable
())
{
// There's a sample available to be read from the current chunk.
return
true
;
haveSamples
=
true
;
}
else
{
// It may be the case that the current chunk has been fully read but not yet discarded and
// that the next chunk has an available sample. Return true if so, otherwise false.
return
mediaChunks
.
size
()
>
1
&&
mediaChunks
.
get
(
1
).
sampleAvailable
();
haveSamples
=
mediaChunks
.
size
()
>
1
&&
mediaChunks
.
get
(
1
).
sampleAvailable
();
}
if
(!
haveSamples
)
{
maybeThrowLoadableException
();
}
return
haveSamples
;
}
@Override
...
...
@@ -380,7 +387,8 @@ public class ChunkSampleSource implements SampleSource, Loader.Callback {
}
private
void
maybeThrowLoadableException
()
throws
IOException
{
if
(
currentLoadableException
!=
null
&&
currentLoadableExceptionCount
>
minLoadableRetryCount
)
{
if
(
currentLoadableException
!=
null
&&
(
currentLoadableExceptionFatal
||
currentLoadableExceptionCount
>
minLoadableRetryCount
))
{
throw
currentLoadableException
;
}
}
...
...
library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java
View file @
4c29eb58
...
...
@@ -326,10 +326,13 @@ public class DashChunkSource implements ChunkSource {
return
;
}
int
lastSegmentNum
=
segmentIndex
.
getLastSegmentNum
();
boolean
indexUnbounded
=
lastSegmentNum
==
DashSegmentIndex
.
INDEX_UNBOUNDED
;
int
segmentNum
;
if
(
queue
.
isEmpty
())
{
if
(
currentManifest
.
dynamic
)
{
seekPositionUs
=
getLiveSeekPosition
();
seekPositionUs
=
getLiveSeekPosition
(
indexUnbounded
);
}
segmentNum
=
segmentIndex
.
getSegmentNum
(
seekPositionUs
);
}
else
{
...
...
@@ -337,16 +340,18 @@ public class DashChunkSource implements ChunkSource {
-
representationHolder
.
segmentNumShift
;
}
// TODO: For unbounded manifests, we need to enforce that we don't try and request chunks
// behind or in front of the live window.
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
()
)
{
}
else
if
(
!
indexUnbounded
&&
segmentNum
>
lastSegmentNum
)
{
// This is beyond the last chunk in the current manifest.
finishedCurrentManifest
=
true
;
return
;
}
else
if
(
segmentNum
==
segmentIndex
.
getLastSegmentNum
()
)
{
}
else
if
(
!
indexUnbounded
&&
segmentNum
==
lastSegmentNum
)
{
// This is the last chunk in the current manifest. Mark the manifest as being finished,
// but continue to return the final chunk.
finishedCurrentManifest
=
true
;
...
...
@@ -452,10 +457,17 @@ public class DashChunkSource implements ChunkSource {
* For live playbacks, determines the seek position that snaps playback to be
* {@link #liveEdgeLatencyUs} behind the live edge of the current manifest
*
* @param indexUnbounded True if the segment index for this source is unbounded. False otherwise.
* @return The seek position in microseconds.
*/
private
long
getLiveSeekPosition
()
{
long
liveEdgeTimestampUs
=
Long
.
MIN_VALUE
;
private
long
getLiveSeekPosition
(
boolean
indexUnbounded
)
{
long
liveEdgeTimestampUs
;
if
(
indexUnbounded
)
{
// TODO: Use UtcTimingElement where possible.
long
nowMs
=
System
.
currentTimeMillis
();
liveEdgeTimestampUs
=
(
nowMs
-
currentManifest
.
availabilityStartTime
)
*
1000
;
}
else
{
liveEdgeTimestampUs
=
Long
.
MIN_VALUE
;
for
(
RepresentationHolder
representationHolder
:
representationHolders
.
values
())
{
DashSegmentIndex
segmentIndex
=
representationHolder
.
segmentIndex
;
int
lastSegmentNum
=
segmentIndex
.
getLastSegmentNum
();
...
...
@@ -463,6 +475,7 @@ public class DashChunkSource implements ChunkSource {
+
segmentIndex
.
getDurationUs
(
lastSegmentNum
);
liveEdgeTimestampUs
=
Math
.
max
(
liveEdgeTimestampUs
,
indexLiveEdgeTimestampUs
);
}
}
return
liveEdgeTimestampUs
-
liveEdgeLatencyUs
;
}
...
...
library/src/main/java/com/google/android/exoplayer/dash/DashSegmentIndex.java
View file @
4c29eb58
...
...
@@ -24,6 +24,8 @@ import com.google.android.exoplayer.dash.mpd.RangedUri;
*/
public
interface
DashSegmentIndex
{
public
static
final
int
INDEX_UNBOUNDED
=
-
1
;
/**
* Returns the segment number of the segment containing a given media time.
*
...
...
@@ -64,9 +66,15 @@ public interface DashSegmentIndex {
int
getFirstSegmentNum
();
/**
* Returns the segment number of the last segment.
* Returns the segment number of the last segment, or {@link #INDEX_UNBOUNDED}.
* <p>
* An unbounded index occurs if a live stream manifest uses SegmentTemplate elements without a
* SegmentTimeline element. In this case the manifest can be used to derive information about
* segments arbitrarily far into the future. This means that the manifest does not need to be
* refreshed as frequently (if at all) during playback, however it is necessary for a player to
* manually calculate the window of currently available segments.
*
* @return The segment number of the last segment.
* @return The segment number of the last segment
, or {@link #INDEX_UNBOUNDED}
.
*/
int
getLastSegmentNum
();
...
...
library/src/main/java/com/google/android/exoplayer/dash/mpd/MediaPresentationDescriptionParser.java
View file @
4c29eb58
...
...
@@ -356,7 +356,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
}
protected
SegmentList
parseSegmentList
(
XmlPullParser
xpp
,
Uri
baseUrl
,
SegmentList
parent
,
long
periodDuration
)
throws
XmlPullParserException
,
IOException
{
long
periodDuration
Ms
)
throws
XmlPullParserException
,
IOException
{
long
timescale
=
parseLong
(
xpp
,
"timescale"
,
parent
!=
null
?
parent
.
timescale
:
1
);
long
presentationTimeOffset
=
parseLong
(
xpp
,
"presentationTimeOffset"
,
...
...
@@ -388,19 +388,19 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
segments
=
segments
!=
null
?
segments
:
parent
.
mediaSegments
;
}
return
buildSegmentList
(
initialization
,
timescale
,
presentationTimeOffset
,
periodDuration
,
return
buildSegmentList
(
initialization
,
timescale
,
presentationTimeOffset
,
periodDuration
Ms
,
startNumber
,
duration
,
timeline
,
segments
);
}
protected
SegmentList
buildSegmentList
(
RangedUri
initialization
,
long
timescale
,
long
presentationTimeOffset
,
long
periodDuration
,
int
startNumber
,
long
duration
,
long
presentationTimeOffset
,
long
periodDuration
Ms
,
int
startNumber
,
long
duration
,
List
<
SegmentTimelineElement
>
timeline
,
List
<
RangedUri
>
segments
)
{
return
new
SegmentList
(
initialization
,
timescale
,
presentationTimeOffset
,
periodDuration
,
return
new
SegmentList
(
initialization
,
timescale
,
presentationTimeOffset
,
periodDuration
Ms
,
startNumber
,
duration
,
timeline
,
segments
);
}
protected
SegmentTemplate
parseSegmentTemplate
(
XmlPullParser
xpp
,
Uri
baseUrl
,
SegmentTemplate
parent
,
long
periodDuration
)
throws
XmlPullParserException
,
IOException
{
SegmentTemplate
parent
,
long
periodDuration
Ms
)
throws
XmlPullParserException
,
IOException
{
long
timescale
=
parseLong
(
xpp
,
"timescale"
,
parent
!=
null
?
parent
.
timescale
:
1
);
long
presentationTimeOffset
=
parseLong
(
xpp
,
"presentationTimeOffset"
,
...
...
@@ -429,15 +429,15 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
timeline
=
timeline
!=
null
?
timeline
:
parent
.
segmentTimeline
;
}
return
buildSegmentTemplate
(
initialization
,
timescale
,
presentationTimeOffset
,
periodDuration
,
return
buildSegmentTemplate
(
initialization
,
timescale
,
presentationTimeOffset
,
periodDuration
Ms
,
startNumber
,
duration
,
timeline
,
initializationTemplate
,
mediaTemplate
,
baseUrl
);
}
protected
SegmentTemplate
buildSegmentTemplate
(
RangedUri
initialization
,
long
timescale
,
long
presentationTimeOffset
,
long
periodDuration
,
int
startNumber
,
long
duration
,
long
presentationTimeOffset
,
long
periodDuration
Ms
,
int
startNumber
,
long
duration
,
List
<
SegmentTimelineElement
>
timeline
,
UrlTemplate
initializationTemplate
,
UrlTemplate
mediaTemplate
,
Uri
baseUrl
)
{
return
new
SegmentTemplate
(
initialization
,
timescale
,
presentationTimeOffset
,
periodDuration
,
return
new
SegmentTemplate
(
initialization
,
timescale
,
presentationTimeOffset
,
periodDuration
Ms
,
startNumber
,
duration
,
timeline
,
initializationTemplate
,
mediaTemplate
,
baseUrl
);
}
...
...
library/src/main/java/com/google/android/exoplayer/dash/mpd/SegmentBase.java
View file @
4c29eb58
...
...
@@ -16,6 +16,7 @@
package
com
.
google
.
android
.
exoplayer
.
dash
.
mpd
;
import
com.google.android.exoplayer.C
;
import
com.google.android.exoplayer.dash.DashSegmentIndex
;
import
com.google.android.exoplayer.util.Util
;
import
android.net.Uri
;
...
...
@@ -127,17 +128,28 @@ public abstract class SegmentBase {
this
.
segmentTimeline
=
segmentTimeline
;
}
public
final
int
getSegmentNum
(
long
timeUs
)
{
// TODO: Optimize this
int
index
=
startNumber
;
while
(
index
+
1
<=
getLastSegmentNum
())
{
if
(
getSegmentTimeUs
(
index
+
1
)
<=
timeUs
)
{
index
++;
public
int
getSegmentNum
(
long
timeUs
)
{
if
(
segmentTimeline
==
null
)
{
// All segments are of equal duration (with the possible exception of the last one).
long
durationUs
=
(
duration
*
C
.
MICROS_PER_SECOND
)
/
timescale
;
return
startNumber
+
(
int
)
(
timeUs
/
durationUs
);
}
else
{
return
index
;
// Identify the segment using binary search.
int
lowIndex
=
getFirstSegmentNum
();
int
highIndex
=
getLastSegmentNum
();
while
(
lowIndex
<=
highIndex
)
{
int
midIndex
=
(
lowIndex
+
highIndex
)
/
2
;
long
midTimeUs
=
getSegmentTimeUs
(
midIndex
);
if
(
midTimeUs
<
timeUs
)
{
lowIndex
=
midIndex
+
1
;
}
else
if
(
midTimeUs
>
timeUs
)
{
highIndex
=
midIndex
-
1
;
}
else
{
return
midIndex
;
}
}
return
lowIndex
-
1
;
}
return
index
;
}
public
final
long
getSegmentDurationUs
(
int
sequenceNumber
)
{
...
...
@@ -285,6 +297,8 @@ public abstract class SegmentBase {
public
int
getLastSegmentNum
()
{
if
(
segmentTimeline
!=
null
)
{
return
segmentTimeline
.
size
()
+
startNumber
-
1
;
}
else
if
(
periodDurationMs
==
-
1
)
{
return
DashSegmentIndex
.
INDEX_UNBOUNDED
;
}
else
{
long
durationMs
=
(
duration
*
1000
)
/
timescale
;
return
startNumber
+
(
int
)
(
periodDurationMs
/
durationMs
);
...
...
library/src/main/java/com/google/android/exoplayer/parser/mp4/Atom.java
View file @
4c29eb58
...
...
@@ -58,6 +58,7 @@ import java.util.ArrayList;
public
static
final
int
TYPE_uuid
=
0x75756964
;
public
static
final
int
TYPE_senc
=
0x73656E63
;
public
static
final
int
TYPE_pasp
=
0x70617370
;
public
static
final
int
TYPE_TTML
=
0x54544D4C
;
public
final
int
type
;
...
...
library/src/main/java/com/google/android/exoplayer/parser/mp4/FragmentedMp4Extractor.java
View file @
4c29eb58
...
...
@@ -428,7 +428,8 @@ public final class FragmentedMp4Extractor implements Extractor {
private
static
Track
parseTrak
(
ContainerAtom
trak
)
{
ContainerAtom
mdia
=
trak
.
getContainerAtomOfType
(
Atom
.
TYPE_mdia
);
int
trackType
=
parseHdlr
(
mdia
.
getLeafAtomOfType
(
Atom
.
TYPE_hdlr
).
data
);
Assertions
.
checkState
(
trackType
==
Track
.
TYPE_AUDIO
||
trackType
==
Track
.
TYPE_VIDEO
);
Assertions
.
checkState
(
trackType
==
Track
.
TYPE_AUDIO
||
trackType
==
Track
.
TYPE_VIDEO
||
trackType
==
Track
.
TYPE_TEXT
);
Pair
<
Integer
,
Long
>
header
=
parseTkhd
(
trak
.
getLeafAtomOfType
(
Atom
.
TYPE_tkhd
).
data
);
int
id
=
header
.
first
;
...
...
@@ -528,6 +529,8 @@ public final class FragmentedMp4Extractor implements Extractor {
parseAudioSampleEntry
(
stsd
,
childAtomType
,
childStartPosition
,
childAtomSize
);
mediaFormat
=
audioSampleEntry
.
first
;
trackEncryptionBoxes
[
i
]
=
audioSampleEntry
.
second
;
}
else
if
(
childAtomType
==
Atom
.
TYPE_TTML
)
{
mediaFormat
=
MediaFormat
.
createTtmlFormat
();
}
stsd
.
setPosition
(
childStartPosition
+
childAtomSize
);
}
...
...
library/src/main/java/com/google/android/exoplayer/parser/mp4/Track.java
View file @
4c29eb58
...
...
@@ -31,6 +31,10 @@ public final class Track {
*/
public
static
final
int
TYPE_AUDIO
=
0x736F756E
;
/**
* Type of a text track.
*/
public
static
final
int
TYPE_TEXT
=
0x74657874
;
/**
* Type of a hint track.
*/
public
static
final
int
TYPE_HINT
=
0x68696E74
;
...
...
library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingChunkSource.java
View file @
4c29eb58
...
...
@@ -358,8 +358,9 @@ public class SmoothStreamingChunkSource implements ChunkSource {
MediaFormat
format
=
MediaFormat
.
createAudioFormat
(
mimeType
,
-
1
,
trackElement
.
numChannels
,
trackElement
.
sampleRate
,
csd
);
return
format
;
}
else
if
(
streamElement
.
type
==
StreamElement
.
TYPE_TEXT
)
{
return
MediaFormat
.
createFormatForMimeType
(
streamElement
.
tracks
[
trackIndex
].
mimeType
);
}
// TODO: Do subtitles need a format? MediaFormat supports KEY_LANGUAGE.
return
null
;
}
...
...
library/src/main/java/com/google/android/exoplayer/text/TextTrackRenderer.java
View file @
4c29eb58
...
...
@@ -58,8 +58,9 @@ public class TextTrackRenderer extends TrackRenderer implements Callback {
private
final
TextRenderer
textRenderer
;
private
final
SampleSource
source
;
private
final
MediaFormatHolder
formatHolder
;
private
final
SubtitleParser
subtitleParser
;
private
final
SubtitleParser
[]
subtitleParsers
;
private
int
parserIndex
;
private
int
trackIndex
;
private
long
currentPositionUs
;
...
...
@@ -73,21 +74,22 @@ public class TextTrackRenderer extends TrackRenderer implements Callback {
/**
* @param source A source from which samples containing subtitle data can be read.
* @param subtitleParser A subtitle parser that will parse Subtitle objects from the source.
* @param textRenderer The text renderer.
* @param textRendererLooper The looper associated with the thread on which textRenderer should be
* invoked. If the renderer makes use of standard Android UI components, then this should
* normally be the looper associated with the applications' main thread, which can be
* obtained using {@link android.app.Activity#getMainLooper()}. Null may be passed if the
* renderer should be invoked directly on the player's internal rendering thread.
* @param subtitleParsers An array of available subtitle parsers. Where multiple parsers are able
* to render a subtitle, the one with the lowest index will be preferred.
*/
public
TextTrackRenderer
(
SampleSource
source
,
SubtitleParser
subtitlePars
er
,
TextRenderer
textRenderer
,
Looper
textRendererLooper
)
{
public
TextTrackRenderer
(
SampleSource
source
,
TextRenderer
textRender
er
,
Looper
textRendererLooper
,
SubtitleParser
...
subtitleParsers
)
{
this
.
source
=
Assertions
.
checkNotNull
(
source
);
this
.
subtitleParser
=
Assertions
.
checkNotNull
(
subtitleParser
);
this
.
textRenderer
=
Assertions
.
checkNotNull
(
textRenderer
);
this
.
textRendererHandler
=
textRendererLooper
==
null
?
null
:
new
Handler
(
textRendererLooper
,
this
);
this
.
textRendererHandler
=
textRendererLooper
==
null
?
null
:
new
Handler
(
textRendererLooper
,
this
);
this
.
subtitleParsers
=
Assertions
.
checkNotNull
(
subtitleParsers
);
formatHolder
=
new
MediaFormatHolder
();
}
...
...
@@ -101,12 +103,15 @@ public class TextTrackRenderer extends TrackRenderer implements Callback {
}
catch
(
IOException
e
)
{
throw
new
ExoPlaybackException
(
e
);
}
for
(
int
i
=
0
;
i
<
source
.
getTrackCount
();
i
++)
{
if
(
subtitleParser
.
canParse
(
source
.
getTrackInfo
(
i
).
mimeType
))
{
trackIndex
=
i
;
for
(
int
i
=
0
;
i
<
subtitleParsers
.
length
;
i
++)
{
for
(
int
j
=
0
;
j
<
source
.
getTrackCount
();
j
++)
{
if
(
subtitleParsers
[
i
].
canParse
(
source
.
getTrackInfo
(
j
).
mimeType
))
{
parserIndex
=
i
;
trackIndex
=
j
;
return
TrackRenderer
.
STATE_PREPARED
;
}
}
}
return
TrackRenderer
.
STATE_IGNORE
;
}
...
...
@@ -115,7 +120,7 @@ public class TextTrackRenderer extends TrackRenderer implements Callback {
source
.
enable
(
trackIndex
,
positionUs
);
parserThread
=
new
HandlerThread
(
"textParser"
);
parserThread
.
start
();
parserHelper
=
new
SubtitleParserHelper
(
parserThread
.
getLooper
(),
subtitleParser
);
parserHelper
=
new
SubtitleParserHelper
(
parserThread
.
getLooper
(),
subtitleParser
s
[
parserIndex
]
);
seekToInternal
(
positionUs
);
}
...
...
@@ -189,6 +194,7 @@ public class TextTrackRenderer extends TrackRenderer implements Callback {
int
result
=
source
.
readData
(
trackIndex
,
positionUs
,
formatHolder
,
sampleHolder
,
false
);
if
(
result
==
SampleSource
.
SAMPLE_READ
)
{
parserHelper
.
startParseOperation
();
textRendererNeedsUpdate
=
false
;
}
else
if
(
result
==
SampleSource
.
END_OF_STREAM
)
{
inputStreamEnded
=
true
;
}
...
...
library/src/main/java/com/google/android/exoplayer/text/webvtt/WebvttParser.java
View file @
4c29eb58
...
...
@@ -32,7 +32,6 @@ import java.util.regex.Pattern;
* A simple WebVTT parser.
* <p>
* @see <a href="http://dev.w3.org/html5/webvtt">WebVTT specification</a>
* <p>
*/
public
class
WebvttParser
implements
SubtitleParser
{
...
...
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