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
fc785871
authored
Apr 04, 2020
by
bachinger
Committed by
Oliver Woodman
Apr 06, 2020
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
add start and end position to media item
PiperOrigin-RevId: 304795753
parent
8583cd92
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
283 additions
and
5 deletions
library/core/src/main/java/com/google/android/exoplayer2/MediaItem.java
library/core/src/main/java/com/google/android/exoplayer2/source/DefaultMediaSourceFactory.java
library/core/src/test/java/com/google/android/exoplayer2/MediaItemTest.java
library/core/src/test/java/com/google/android/exoplayer2/source/DefaultMediaSourceFactoryTest.java
library/core/src/main/java/com/google/android/exoplayer2/MediaItem.java
View file @
fc785871
...
...
@@ -57,6 +57,11 @@ public final class MediaItem {
@Nullable
private
String
mediaId
;
@Nullable
private
Uri
sourceUri
;
@Nullable
private
String
mimeType
;
private
long
clipStartPositionMs
;
private
long
clipEndPositionMs
;
private
boolean
clipRelativeToLiveWindow
;
private
boolean
clipRelativeToDefaultPosition
;
private
boolean
clipStartsAtKeyFrame
;
@Nullable
private
Uri
drmLicenseUri
;
private
Map
<
String
,
String
>
drmLicenseRequestHeaders
;
@Nullable
private
UUID
drmUuid
;
...
...
@@ -74,6 +79,7 @@ public final class MediaItem {
subtitles
=
Collections
.
emptyList
();
drmSessionForClearTypes
=
Collections
.
emptyList
();
drmLicenseRequestHeaders
=
Collections
.
emptyMap
();
clipEndPositionMs
=
C
.
TIME_END_OF_SOURCE
;
}
/**
...
...
@@ -118,6 +124,55 @@ public final class MediaItem {
}
/**
* Sets the optional start position in milliseconds which must be a value larger than or equal
* to zero (Default: 0).
*/
public
Builder
setClipStartPositionMs
(
long
startPositionMs
)
{
Assertions
.
checkArgument
(
startPositionMs
>=
0
);
this
.
clipStartPositionMs
=
startPositionMs
;
return
this
;
}
/**
* Sets the optional end position in milliseconds which must be a value larger than or equal to
* zero, or {@link C#TIME_END_OF_SOURCE} to end when playback reaches the end of media (Default:
* {@link C#TIME_END_OF_SOURCE}).
*/
public
Builder
setClipEndPositionMs
(
long
endPositionMs
)
{
Assertions
.
checkArgument
(
endPositionMs
==
C
.
TIME_END_OF_SOURCE
||
endPositionMs
>=
0
);
this
.
clipEndPositionMs
=
endPositionMs
;
return
this
;
}
/**
* Sets whether the start/end positions should move with the live window for live streams. If
* {@code false}, live streams end when playback reaches the end position in live window seen
* when the media is first loaded (Default: {@code false}).
*/
public
Builder
setClipRelativeToLiveWindow
(
boolean
relativeToLiveWindow
)
{
this
.
clipRelativeToLiveWindow
=
relativeToLiveWindow
;
return
this
;
}
/**
* Sets whether the start position and the end position are relative to the default position in
* the window (Default: {@code false}).
*/
public
Builder
setClipRelativeToDefaultPosition
(
boolean
relativeToDefaultPosition
)
{
this
.
clipRelativeToDefaultPosition
=
relativeToDefaultPosition
;
return
this
;
}
/**
* Sets whether the start point is guaranteed to be a key frame. If {@code false}, the playback
* transition into the clip may not be seamless (Default: {@code false}).
*/
public
Builder
setClipStartsAtKeyFrame
(
boolean
startsAtKeyFrame
)
{
this
.
clipStartsAtKeyFrame
=
startsAtKeyFrame
;
return
this
;
}
/**
* Sets the optional license server {@link Uri}. If a license uri is set, the {@link
* DrmConfiguration#uuid} needs to be specified as well.
*
...
...
@@ -303,6 +358,12 @@ public final class MediaItem {
}
return
new
MediaItem
(
Assertions
.
checkNotNull
(
mediaId
),
new
ClippingProperties
(
clipStartPositionMs
,
clipEndPositionMs
,
clipRelativeToLiveWindow
,
clipRelativeToDefaultPosition
,
clipStartsAtKeyFrame
),
playbackProperties
,
mediaMetadata
!=
null
?
mediaMetadata
:
new
MediaMetadata
.
Builder
().
build
());
}
...
...
@@ -521,6 +582,75 @@ public final class MediaItem {
}
}
/** Optionally clips the media item to a custom start and end position. */
public
static
final
class
ClippingProperties
{
/** The start position in milliseconds. This is a value larger than or equal to zero. */
public
final
long
startPositionMs
;
/**
* The end position in milliseconds. This is a value larger than or equal to zero or {@link
* C#TIME_END_OF_SOURCE} to play to the end of the stream.
*/
public
final
long
endPositionMs
;
/**
* Whether the clipping of active media periods moves with a live window. If {@code false},
* playback ends when it reaches {@link #endPositionMs}.
*/
public
final
boolean
relativeToLiveWindow
;
/**
* Whether {@link #startPositionMs} and {@link #endPositionMs} are relative to the default
* position.
*/
public
final
boolean
relativeToDefaultPosition
;
/** Sets whether the start point is guaranteed to be a key frame. */
public
final
boolean
startsAtKeyFrame
;
private
ClippingProperties
(
long
startPositionMs
,
long
endPositionMs
,
boolean
relativeToLiveWindow
,
boolean
relativeToDefaultPosition
,
boolean
startsAtKeyFrame
)
{
this
.
startPositionMs
=
startPositionMs
;
this
.
endPositionMs
=
endPositionMs
;
this
.
relativeToLiveWindow
=
relativeToLiveWindow
;
this
.
relativeToDefaultPosition
=
relativeToDefaultPosition
;
this
.
startsAtKeyFrame
=
startsAtKeyFrame
;
}
@Override
public
boolean
equals
(
@Nullable
Object
obj
)
{
if
(
this
==
obj
)
{
return
true
;
}
if
(!(
obj
instanceof
ClippingProperties
))
{
return
false
;
}
ClippingProperties
other
=
(
ClippingProperties
)
obj
;
return
startPositionMs
==
other
.
startPositionMs
&&
endPositionMs
==
other
.
endPositionMs
&&
relativeToLiveWindow
==
other
.
relativeToLiveWindow
&&
relativeToDefaultPosition
==
other
.
relativeToDefaultPosition
&&
startsAtKeyFrame
==
other
.
startsAtKeyFrame
;
}
@Override
public
int
hashCode
()
{
int
result
=
Long
.
valueOf
(
startPositionMs
).
hashCode
();
result
=
31
*
result
+
Long
.
valueOf
(
endPositionMs
).
hashCode
();
result
=
31
*
result
+
(
relativeToLiveWindow
?
1
:
0
);
result
=
31
*
result
+
(
relativeToDefaultPosition
?
1
:
0
);
result
=
31
*
result
+
(
startsAtKeyFrame
?
1
:
0
);
return
result
;
}
}
/** Identifies the media item. */
public
final
String
mediaId
;
...
...
@@ -530,13 +660,18 @@ public final class MediaItem {
/** The media metadata. */
public
final
MediaMetadata
mediaMetadata
;
/** The clipping properties. */
public
final
ClippingProperties
clippingProperties
;
private
MediaItem
(
String
mediaId
,
ClippingProperties
clippingProperties
,
@Nullable
PlaybackProperties
playbackProperties
,
MediaMetadata
mediaMetadata
)
{
this
.
mediaId
=
mediaId
;
this
.
playbackProperties
=
playbackProperties
;
this
.
mediaMetadata
=
mediaMetadata
;
this
.
clippingProperties
=
clippingProperties
;
}
@Override
...
...
@@ -551,6 +686,7 @@ public final class MediaItem {
MediaItem
other
=
(
MediaItem
)
obj
;
return
Util
.
areEqual
(
mediaId
,
other
.
mediaId
)
&&
clippingProperties
.
equals
(
other
.
clippingProperties
)
&&
Util
.
areEqual
(
playbackProperties
,
other
.
playbackProperties
)
&&
Util
.
areEqual
(
mediaMetadata
,
other
.
mediaMetadata
);
}
...
...
@@ -559,6 +695,7 @@ public final class MediaItem {
public
int
hashCode
()
{
int
result
=
mediaId
.
hashCode
();
result
=
31
*
result
+
(
playbackProperties
!=
null
?
playbackProperties
.
hashCode
()
:
0
);
result
=
31
*
result
+
clippingProperties
.
hashCode
();
result
=
31
*
result
+
mediaMetadata
.
hashCode
();
return
result
;
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/source/DefaultMediaSourceFactory.java
View file @
fc785871
...
...
@@ -216,11 +216,11 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
MediaSource
leafMediaSource
=
mediaSourceFactory
.
createMediaSource
(
mediaItem
);
if
(
mediaItem
.
playbackProperties
.
subtitles
.
isEmpty
())
{
return
leafMediaSource
;
List
<
MediaItem
.
Subtitle
>
subtitles
=
mediaItem
.
playbackProperties
.
subtitles
;
if
(
subtitles
.
isEmpty
())
{
return
maybeClipMediaSource
(
mediaItem
,
leafMediaSource
);
}
List
<
MediaItem
.
Subtitle
>
subtitles
=
mediaItem
.
playbackProperties
.
subtitles
;
MediaSource
[]
mediaSources
=
new
MediaSource
[
subtitles
.
size
()
+
1
];
mediaSources
[
0
]
=
leafMediaSource
;
SingleSampleMediaSource
.
Factory
singleSampleSourceFactory
=
...
...
@@ -234,9 +234,10 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
.
setSelectionFlags
(
subtitle
.
selectionFlags
)
.
build
();
mediaSources
[
i
+
1
]
=
singleSampleSourceFactory
.
createMediaSource
(
subtitle
.
uri
,
subtitleFormat
,
C
.
TIME_UNSET
);
singleSampleSourceFactory
.
createMediaSource
(
subtitle
.
uri
,
subtitleFormat
,
/* durationUs= */
C
.
TIME_UNSET
);
}
return
new
MergingMediaSource
(
mediaSources
);
return
maybeClipMediaSource
(
mediaItem
,
new
MergingMediaSource
(
mediaSources
)
);
}
// internal methods
...
...
@@ -269,6 +270,21 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
return
drmCallback
;
}
private
static
MediaSource
maybeClipMediaSource
(
MediaItem
mediaItem
,
MediaSource
mediaSource
)
{
if
(
mediaItem
.
clippingProperties
.
startPositionMs
==
0
&&
mediaItem
.
clippingProperties
.
endPositionMs
==
C
.
TIME_END_OF_SOURCE
&&
!
mediaItem
.
clippingProperties
.
relativeToDefaultPosition
)
{
return
mediaSource
;
}
return
new
ClippingMediaSource
(
mediaSource
,
C
.
msToUs
(
mediaItem
.
clippingProperties
.
startPositionMs
),
C
.
msToUs
(
mediaItem
.
clippingProperties
.
endPositionMs
),
/* enableInitialDiscontinuity= */
!
mediaItem
.
clippingProperties
.
startsAtKeyFrame
,
/* allowDynamicClippingUpdates= */
mediaItem
.
clippingProperties
.
relativeToLiveWindow
,
mediaItem
.
clippingProperties
.
relativeToDefaultPosition
);
}
private
static
SparseArray
<
MediaSourceFactory
>
loadDelegates
(
DataSource
.
Factory
dataSourceFactory
)
{
SparseArray
<
MediaSourceFactory
>
factories
=
new
SparseArray
<>();
...
...
library/core/src/test/java/com/google/android/exoplayer2/MediaItemTest.java
View file @
fc785871
...
...
@@ -188,6 +188,77 @@ public class MediaItemTest {
}
@Test
public
void
builderSetStartPositionMs_setsStartPositionMs
()
{
MediaItem
mediaItem
=
new
MediaItem
.
Builder
().
setSourceUri
(
URI_STRING
).
setClipStartPositionMs
(
1000L
).
build
();
assertThat
(
mediaItem
.
clippingProperties
.
startPositionMs
).
isEqualTo
(
1000L
);
}
@Test
public
void
builderSetStartPositionMs_zeroByDefault
()
{
MediaItem
mediaItem
=
new
MediaItem
.
Builder
().
setSourceUri
(
URI_STRING
).
build
();
assertThat
(
mediaItem
.
clippingProperties
.
startPositionMs
).
isEqualTo
(
0
);
}
@Test
public
void
builderSetStartPositionMs_negativeValue_throws
()
{
MediaItem
.
Builder
builder
=
new
MediaItem
.
Builder
();
assertThrows
(
IllegalArgumentException
.
class
,
()
->
builder
.
setClipStartPositionMs
(-
1
));
}
@Test
public
void
builderSetEndPositionMs_setsEndPositionMs
()
{
MediaItem
mediaItem
=
new
MediaItem
.
Builder
().
setSourceUri
(
URI_STRING
).
setClipEndPositionMs
(
1000L
).
build
();
assertThat
(
mediaItem
.
clippingProperties
.
endPositionMs
).
isEqualTo
(
1000L
);
}
@Test
public
void
builderSetEndPositionMs_timeEndOfSourceByDefault
()
{
MediaItem
mediaItem
=
new
MediaItem
.
Builder
().
setSourceUri
(
URI_STRING
).
build
();
assertThat
(
mediaItem
.
clippingProperties
.
endPositionMs
).
isEqualTo
(
C
.
TIME_END_OF_SOURCE
);
}
@Test
public
void
builderSetEndPositionMs_timeEndOfSource_setsEndPositionMs
()
{
MediaItem
mediaItem
=
new
MediaItem
.
Builder
()
.
setSourceUri
(
URI_STRING
)
.
setClipEndPositionMs
(
1000
)
.
setClipEndPositionMs
(
C
.
TIME_END_OF_SOURCE
)
.
build
();
assertThat
(
mediaItem
.
clippingProperties
.
endPositionMs
).
isEqualTo
(
C
.
TIME_END_OF_SOURCE
);
}
@Test
public
void
builderSetEndPositionMs_negativeValue_throws
()
{
MediaItem
.
Builder
builder
=
new
MediaItem
.
Builder
();
assertThrows
(
IllegalArgumentException
.
class
,
()
->
builder
.
setClipEndPositionMs
(-
1
));
}
@Test
public
void
builderSetClippingFlags_setsClippingFlags
()
{
MediaItem
mediaItem
=
new
MediaItem
.
Builder
()
.
setSourceUri
(
URI_STRING
)
.
setClipRelativeToDefaultPosition
(
true
)
.
setClipRelativeToLiveWindow
(
true
)
.
setClipStartsAtKeyFrame
(
true
)
.
build
();
assertThat
(
mediaItem
.
clippingProperties
.
relativeToDefaultPosition
).
isTrue
();
assertThat
(
mediaItem
.
clippingProperties
.
relativeToLiveWindow
).
isTrue
();
assertThat
(
mediaItem
.
clippingProperties
.
startsAtKeyFrame
).
isTrue
();
}
@Test
public
void
builderSetMediaMetadata_setsMetadata
()
{
MediaMetadata
mediaMetadata
=
new
MediaMetadata
.
Builder
().
setTitle
(
"title"
).
build
();
...
...
library/core/src/test/java/com/google/android/exoplayer2/source/DefaultMediaSourceFactoryTest.java
View file @
fc785871
...
...
@@ -123,6 +123,60 @@ public final class DefaultMediaSourceFactoryTest {
}
@Test
public
void
createMediaSource_withStartPosition_isClippingMediaSource
()
{
DefaultMediaSourceFactory
defaultMediaSourceFactory
=
DefaultMediaSourceFactory
.
newInstance
(
ApplicationProvider
.
getApplicationContext
());
MediaItem
mediaItem
=
new
MediaItem
.
Builder
().
setSourceUri
(
URI_MEDIA
).
setClipStartPositionMs
(
1000L
).
build
();
MediaSource
mediaSource
=
defaultMediaSourceFactory
.
createMediaSource
(
mediaItem
);
assertThat
(
mediaSource
).
isInstanceOf
(
ClippingMediaSource
.
class
);
}
@Test
public
void
createMediaSource_withEndPosition_isClippingMediaSource
()
{
DefaultMediaSourceFactory
defaultMediaSourceFactory
=
DefaultMediaSourceFactory
.
newInstance
(
ApplicationProvider
.
getApplicationContext
());
MediaItem
mediaItem
=
new
MediaItem
.
Builder
().
setSourceUri
(
URI_MEDIA
).
setClipEndPositionMs
(
1000L
).
build
();
MediaSource
mediaSource
=
defaultMediaSourceFactory
.
createMediaSource
(
mediaItem
);
assertThat
(
mediaSource
).
isInstanceOf
(
ClippingMediaSource
.
class
);
}
@Test
public
void
createMediaSource_relativeToDefaultPosition_isClippingMediaSource
()
{
DefaultMediaSourceFactory
defaultMediaSourceFactory
=
DefaultMediaSourceFactory
.
newInstance
(
ApplicationProvider
.
getApplicationContext
());
MediaItem
mediaItem
=
new
MediaItem
.
Builder
()
.
setSourceUri
(
URI_MEDIA
)
.
setClipRelativeToDefaultPosition
(
true
)
.
build
();
MediaSource
mediaSource
=
defaultMediaSourceFactory
.
createMediaSource
(
mediaItem
);
assertThat
(
mediaSource
).
isInstanceOf
(
ClippingMediaSource
.
class
);
}
@Test
public
void
createMediaSource_defaultToEnd_isNotClippingMediaSource
()
{
DefaultMediaSourceFactory
defaultMediaSourceFactory
=
DefaultMediaSourceFactory
.
newInstance
(
ApplicationProvider
.
getApplicationContext
());
MediaItem
mediaItem
=
new
MediaItem
.
Builder
()
.
setSourceUri
(
URI_MEDIA
)
.
setClipEndPositionMs
(
C
.
TIME_END_OF_SOURCE
)
.
build
();
MediaSource
mediaSource
=
defaultMediaSourceFactory
.
createMediaSource
(
mediaItem
);
assertThat
(
mediaSource
).
isInstanceOf
(
ProgressiveMediaSource
.
class
);
}
@Test
public
void
getSupportedTypes_coreModule_onlyOther
()
{
int
[]
supportedTypes
=
DefaultMediaSourceFactory
.
newInstance
(
ApplicationProvider
.
getApplicationContext
())
...
...
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