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
9316b4f4
authored
Dec 15, 2021
by
bachinger
Committed by
Ian Baker
Dec 16, 2021
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Add helper method to split the AdPlaybackState
PiperOrigin-RevId: 416543473
parent
66f7657a
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
128 additions
and
0 deletions
extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaUtil.java
extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/ImaUtilTest.java
extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaUtil.java
View file @
9316b4f4
...
...
@@ -15,6 +15,9 @@
*/
package
com
.
google
.
android
.
exoplayer2
.
ext
.
ima
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Assertions
.
checkNotNull
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Assertions
.
checkState
;
import
android.content.Context
;
import
android.os.Looper
;
import
android.view.View
;
...
...
@@ -36,6 +39,8 @@ import com.google.ads.interactivemedia.v3.api.UiElement;
import
com.google.ads.interactivemedia.v3.api.player.VideoAdPlayer
;
import
com.google.ads.interactivemedia.v3.api.player.VideoProgressUpdate
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.Timeline
;
import
com.google.android.exoplayer2.source.ads.AdPlaybackState
;
import
com.google.android.exoplayer2.ui.AdOverlayInfo
;
import
com.google.android.exoplayer2.ui.AdViewProvider
;
import
com.google.android.exoplayer2.upstream.DataSchemeDataSource
;
...
...
@@ -43,10 +48,13 @@ import com.google.android.exoplayer2.upstream.DataSourceUtil;
import
com.google.android.exoplayer2.upstream.DataSpec
;
import
com.google.android.exoplayer2.util.Util
;
import
com.google.common.collect.ImmutableList
;
import
com.google.common.collect.ImmutableMap
;
import
java.io.IOException
;
import
java.util.Arrays
;
import
java.util.Collection
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Set
;
/** Utilities for working with IMA SDK and IMA extension data types. */
...
...
@@ -255,5 +263,125 @@ import java.util.Set;
}
}
/**
* Splits an {@link AdPlaybackState} into a separate {@link AdPlaybackState} for each period of a
* content timeline. Ad group times are expected to not take previous ad duration into account and
* needs to be translated to the actual position in the {@code contentTimeline} by adding prior ad
* durations.
*
* <p>If a period is enclosed by an ad group, the period is considered an ad period and gets an ad
* playback state assigned with a single ad in a single ad group. The duration of the ad is set to
* the duration of the period. All other periods are considered content periods with an empty ad
* playback state without any ads.
*
* @param adPlaybackState The ad playback state to be split.
* @param contentTimeline The content timeline for each period of which to create an {@link
* AdPlaybackState}.
* @return A map of ad playback states for each period UID in the content timeline.
*/
public
static
ImmutableMap
<
Object
,
AdPlaybackState
>
splitAdPlaybackStateForPeriods
(
AdPlaybackState
adPlaybackState
,
Timeline
contentTimeline
)
{
Timeline
.
Period
period
=
new
Timeline
.
Period
();
if
(
contentTimeline
.
getPeriodCount
()
==
1
)
{
// A single period gets the entire ad playback state that may contain multiple ad groups.
return
ImmutableMap
.
of
(
checkNotNull
(
contentTimeline
.
getPeriod
(
/* periodIndex= */
0
,
period
,
/* setIds= */
true
).
uid
),
adPlaybackState
);
}
int
periodIndex
=
0
;
long
totalElapsedContentDurationUs
=
0
;
Object
adsId
=
checkNotNull
(
adPlaybackState
.
adsId
);
AdPlaybackState
contentOnlyAdPlaybackState
=
new
AdPlaybackState
(
adsId
);
Map
<
Object
,
AdPlaybackState
>
adPlaybackStates
=
new
HashMap
<>();
for
(
int
i
=
adPlaybackState
.
removedAdGroupCount
;
i
<
adPlaybackState
.
adGroupCount
;
i
++)
{
AdPlaybackState
.
AdGroup
adGroup
=
adPlaybackState
.
getAdGroup
(
/* adGroupIndex= */
i
);
if
(
adGroup
.
timeUs
==
C
.
TIME_END_OF_SOURCE
)
{
checkState
(
i
==
adPlaybackState
.
adGroupCount
-
1
);
// The last ad group is a placeholder for a potential post roll. We can just stop here.
break
;
}
// The ad group start timeUs is in content position. We need to add the ad
// duration before the ad group to translate the start time to the position in the period.
long
adGroupDurationUs
=
getTotalDurationUs
(
adGroup
.
durationsUs
);
long
elapsedAdGroupAdDurationUs
=
0
;
for
(
int
j
=
periodIndex
;
j
<
contentTimeline
.
getPeriodCount
();
j
++)
{
contentTimeline
.
getPeriod
(
j
,
period
,
/* setIds= */
true
);
if
(
totalElapsedContentDurationUs
<
adGroup
.
timeUs
)
{
// Period starts before the ad group, so it is a content period.
adPlaybackStates
.
put
(
checkNotNull
(
period
.
uid
),
contentOnlyAdPlaybackState
);
totalElapsedContentDurationUs
+=
period
.
durationUs
;
}
else
{
long
periodStartUs
=
totalElapsedContentDurationUs
+
elapsedAdGroupAdDurationUs
;
if
(
periodStartUs
+
period
.
durationUs
<=
adGroup
.
timeUs
+
adGroupDurationUs
)
{
// The period ends before the end of the ad group, so it is an ad period (Note: An ad
// reported by the IMA SDK may span multiple periods).
adPlaybackStates
.
put
(
checkNotNull
(
period
.
uid
),
splitAdGroupForPeriod
(
adsId
,
adGroup
,
periodStartUs
,
period
.
durationUs
));
elapsedAdGroupAdDurationUs
+=
period
.
durationUs
;
}
else
{
// Period is after the current ad group. Continue with next ad group.
break
;
}
}
// Increment the period index to the next unclassified period.
periodIndex
++;
}
}
// The remaining periods end after the last ad group, so these are content periods.
for
(
int
i
=
periodIndex
;
i
<
contentTimeline
.
getPeriodCount
();
i
++)
{
contentTimeline
.
getPeriod
(
i
,
period
,
/* setIds= */
true
);
adPlaybackStates
.
put
(
checkNotNull
(
period
.
uid
),
contentOnlyAdPlaybackState
);
}
return
ImmutableMap
.
copyOf
(
adPlaybackStates
);
}
private
static
AdPlaybackState
splitAdGroupForPeriod
(
Object
adsId
,
AdPlaybackState
.
AdGroup
adGroup
,
long
periodStartUs
,
long
periodDurationUs
)
{
checkState
(
adGroup
.
timeUs
<=
periodStartUs
);
AdPlaybackState
adPlaybackState
=
new
AdPlaybackState
(
checkNotNull
(
adsId
),
/* adGroupTimesUs...= */
0
)
.
withAdCount
(
/* adGroupIndex= */
0
,
/* adCount= */
1
)
.
withAdDurationsUs
(
/* adGroupIndex= */
0
,
periodDurationUs
)
.
withIsServerSideInserted
(
/* adGroupIndex= */
0
,
true
);
long
periodEndUs
=
periodStartUs
+
periodDurationUs
;
long
adDurationsUs
=
0
;
for
(
int
i
=
0
;
i
<
adGroup
.
count
;
i
++)
{
adDurationsUs
+=
adGroup
.
durationsUs
[
i
];
if
(
periodEndUs
==
adGroup
.
timeUs
+
adDurationsUs
)
{
// Map the state of the global ad state to the period specific ad state.
switch
(
adGroup
.
states
[
i
])
{
case
AdPlaybackState
.
AD_STATE_PLAYED
:
adPlaybackState
=
adPlaybackState
.
withPlayedAd
(
/* adGroupIndex= */
0
,
/* adIndexInAdGroup= */
0
);
break
;
case
AdPlaybackState
.
AD_STATE_SKIPPED
:
adPlaybackState
=
adPlaybackState
.
withSkippedAd
(
/* adGroupIndex= */
0
,
/* adIndexInAdGroup= */
0
);
break
;
case
AdPlaybackState
.
AD_STATE_ERROR
:
adPlaybackState
=
adPlaybackState
.
withAdLoadError
(
/* adGroupIndex= */
0
,
/* adIndexInAdGroup= */
0
);
break
;
default
:
// Do nothing.
break
;
}
break
;
}
}
return
adPlaybackState
;
}
private
static
long
getTotalDurationUs
(
long
[]
durationsUs
)
{
long
totalDurationUs
=
0
;
for
(
long
adDurationUs
:
durationsUs
)
{
totalDurationUs
+=
adDurationUs
;
}
return
totalDurationUs
;
}
private
ImaUtil
()
{}
}
extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/ImaUtilTest.java
0 → 100644
View file @
9316b4f4
This diff is collapsed.
Click to expand it.
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