Ad Breaks
Stay organized with collections
Save and categorize content based on your preferences.
The Android TV Receiver SDK features native support for Ad Breaks and companion
ads within a given media stream.
See the Web Receiver Ad Breaks Overview
for more information on how Ad Breaks work.
Handling load with ad breaks
On your Android TV app, the breaks are included in MediaLoadRequestData
.
Load requests can be processed normally and the AdBreakClipInfo
s
and AdBreakInfo
s
can be retrieved from the MediaInfo
:
class
MyMediaLoadCommandCallback
:
MediaLoadCommandCallback
()
{
override
fun
onLoad
(
senderId
:
String?
,
loadRequestData
:
MediaLoadRequestData
):
Task
{
return
Tasks
.
call
{
// Resolve the entity into your data structure and load media.
val
mediaInfo
=
loadRequestData
.
mediaInfo
...
myPrepareAdBreaks
(
mediaInfo
.
adBreakClips
,
mediaInfo
.
adBreaks
)
// Update media metadata and state (this clears all previous status
// overrides).
castReceiverContext
.
getMediaStatusModifier
()
.
setDataFromLoad
(
mediaInfo
)
// Ad breaks are set on the modifier.
castReceiverContext
.
getMediaManager
().
broadcastMediaStatus
()
// Return the resolved MediaLoadRequestData to indicate load success.
return
loadRequestData
}
}
}
public
class
MyMediaLoadCommandCallback
extends
MediaLoadCommandCallback
{
@
Override
public
Task
onLoad
(
String
senderId
,
MediaLoadRequestData
loadRequestData
)
{
return
Tasks
.
call
(()
->
{
//
Resolve
the
entity
into
your
data
structure
and
load
media
.
MediaInfo
mediaInfo
=
loadRequestData
.
getMediaInfo
();
...
myPrepareAdBreaks
(
mediaInfo
.
getAdBreakClips
(),
mediaInfo
.
getAdBreaks
());
//
Update
media
metadata
and
state
(
this
clears
all
previous
status
//
overrides
)
.
castReceiverContext
.
getMediaStatusModifier
()
.
setDataFromLoad
(
mediaInfo
);
//
Ad
breaks
are
set
on
the
modifier
.
castReceiverContext
.
getMediaManager
()
.
broadcastMediaStatus
();
//
Return
the
resolved
MediaLoadRequestData
to
indicate
load
success
.
return
loadRequestData
;
});
}
}
Updating ad breaks
When ads start playing, update the AdBreakStatus
on the MediaStatusModifier
to broadcast that your app has started playing ads:
val
breakStatus
=
AdBreakStatus
.
Builder
()
.
setBreakId
(
"b1"
)
.
setBreakClipId
(
"bc1"
)
.
setCurrentBreakClipTimeInMs
(
breakClipProgress
)
.
setCurrentBreakTimeInMs
(
breakProgress
)
.
setWhenSkippableInMs
(
5000
)
// Set this field so that the ad break clip is skippable
.
build
()
castReceiverContext
.
getMediaStatusModifier
()
.
setAdBreakStatus
(
breakStatus
)
AdBreakStatus
breakStatus
=
new
AdBreakStatus
.
Builder
()
.
setBreakId
(
"b1"
)
.
setBreakClipId
(
"bc1"
)
.
setCurrentBreakClipTimeInMs
(
breakClipProgress
)
.
setCurrentBreakTimeInMs
(
breakProgress
)
.
setWhenSkippableInMs
(
5000
)
// Set this field so that the ad break clip is skippable
.
build
();
castReceiverContext
.
getMediaStatusModifier
()
.
setAdBreakStatus
(
breakStatus
);
You can also dynamically modify the ad breaks after an item is loaded:
var
breakClip1
:
AdBreakClipInfo
=
...
var
breakClip2
:
AdBreakClipInfo
=
...
var
breakClip3
:
AdBreakClipInfo
=
...
var
break1
:
AdBreakInfo
=
...
var
break2
:
AdBreakInfo
=
...
mediaManager
.
getMediaStatusModifier
().
getMediaInfoModifier
()
.
setAdBreakClips
({
breakClip1
,
breakClip2
,
breakClip3
})
.
setAdBreaks
({
break1
,
break2
})
AdBreakClipInfo breakClip1 = ...
AdBreakClipInfo breakClip2 = ...
AdBreakClipInfo breakClip3 = ...
AdBreakInfo break1 = ...
AdBreakInfo break2 = ...
mediaManager.getMediaStatusModifier().getMediaInfoModifier()
.setAdBreakClips({breakClip1, breakClip2, breakClip3})
.setAdBreaks({break1, break2});
Enable and handle ad skipping
When an ad break is playing, the senders will show a button to skip the current
ad break clip if it is skippable. To enable the ability for a user to skip an ad
break clip, use the MediaStatusModifier
to add the COMMAND_SKIP_AD
media command:
mMediaManager
.
getMediaStatusModifier
().
setMediaCommandSupported
(
MediaStatus
.
COMMAND_SKIP_AD
,
true
)
mMediaManager.getMediaStatusModifier().setMediaCommandSupported(MediaStatus.COMMAND_SKIP_AD, true);
To handle the SKIP_AD
command, implement the onSkipAd
callback in your MediaCommandCallback
s:
class
MyMediaCommandCallback
:
MediaCommandCallback
()
{
override
fun
onSkipAd
(
requestData
:
RequestData?)
:
Task
<
Void?>
{
// Skip your ad
...
return
Tasks
.
forResult
<
Any?
>
(
null
)
}
}
val
mediaManager
=
CastReceiverContext
.
getInstance
().
mediaManager
mediaManager
.
setMediaCommandCallback
(
MyMediaCommandCallback
())
public
class
MyMediaCommandCallback
extends
MediaCommandCallback
{
@
Override
public
Task
onSkipAd
(
RequestData
requestData
)
{
//
Skip
your
ad
...
return
Tasks
.
forResult
(
null
);
}
}
MediaManager
mediaManager
=
CastReceiverContext
.
getInstance
()
.
getMediaManager
();
mediaManager
.
setMediaCommandCallback
(
new
MyMediaCommandCallback
());
Client-side stitching
Client-side stitching
is where ads are not embedded in the stream. For Cast Connect, in addition to
updating the AdBreakStatus
on the MediaStatusModifier
you mustset the playback speed to 0 on the PlaybackStateCompat
so the senders know to freeze the content timeline progress.
// Playback speed should be 0 if content is not playing.
if
(
adIsPlaying
)
{
playbackSpeed
=
0.0f
}
val
stateBuilder
=
PlaybackStateCompat
.
Builder
()
.
setActions
(
AVAILABLE_MEDIA_ACTIONS
)
stateBuilder
.
setState
(
playbackStateCompat
,
position
,
playbackSpeed
)
mediaSession
.
setPlaybackState
(
stateBuilder
.
build
())
// Playback speed should be 0 if content is not playing.
if
(
adIsPlaying
)
{
playbackSpeed
=
0.0
f
;
}
PlaybackStateCompat
.
Builder
stateBuilder
=
new
PlaybackStateCompat
.
Builder
()
.
setActions
(
AVAILABLE_MEDIA_ACTIONS
);
stateBuilder
.
setState
(
playbackStateCompat
,
position
,
playbackSpeed
);
mediaSession
.
setPlaybackState
(
stateBuilder
.
build
());
Once the ad finishes, you should resume the previous playback speed.
Server-side stitching
For server-side stitching
,
the ads are embedded so the server is expected to provide a single stream that
contains both the content and ads. In this case, playback can continue
progressing normally as the timeline contains the duration of the ad in addition
to the content.
Except as otherwise noted, the content of this page is licensed under the Creative Commons Attribution 4.0 License
, and code samples are licensed under the Apache 2.0 License
. For details, see the Google Developers Site Policies
. Java is a registered trademark of Oracle and/or its affiliates.
Last updated 2025-09-04 UTC.
[[["Easy to understand","easyToUnderstand","thumb-up"],["Solved my problem","solvedMyProblem","thumb-up"],["Other","otherUp","thumb-up"]],[["Missing the information I need","missingTheInformationINeed","thumb-down"],["Too complicated / too many steps","tooComplicatedTooManySteps","thumb-down"],["Out of date","outOfDate","thumb-down"],["Samples / code issue","samplesCodeIssue","thumb-down"],["Other","otherDown","thumb-down"]],["Last updated 2025-09-04 UTC."],[[["\u003cp\u003eThe Android TV Receiver SDK natively supports ad breaks and companion ads, allowing developers to integrate ads into their media streams.\u003c/p\u003e\n"],["\u003cp\u003eDevelopers can handle ad breaks by including them in \u003ccode\u003eMediaLoadRequestData\u003c/code\u003e and retrieving information about them from \u003ccode\u003eMediaInfo\u003c/code\u003e.\u003c/p\u003e\n"],["\u003cp\u003eWhen ads start playing, developers need to update the \u003ccode\u003eAdBreakStatus\u003c/code\u003e to inform the sender app and enable ad skipping by setting \u003ccode\u003eCOMMAND_SKIP_AD\u003c/code\u003e as supported.\u003c/p\u003e\n"],["\u003cp\u003eFor client-side stitching where ads are not embedded, developers must freeze the content timeline progress by setting the playback speed to 0 during ad playback.\u003c/p\u003e\n"],["\u003cp\u003eServer-side stitching embeds ads within the stream, allowing playback to progress normally as the timeline includes ad duration.\u003c/p\u003e\n"]]],["The Android TV Receiver SDK supports ad breaks and companion ads. During media loading, `AdBreakClipInfo` and `AdBreakInfo` are retrievable from `MediaInfo`. When ads begin, `AdBreakStatus` updates are broadcast via `MediaStatusModifier`. Ad breaks can be dynamically modified with `setAdBreakClips` and `setAdBreaks`. To enable ad skipping, set the `COMMAND_SKIP_AD` media command. For client-side ad stitching, playback speed must be set to 0 when ads play, resuming normal speed afterward. For server-side stitching, playback progresses normally.\n"],null,["# Ad Breaks\n\nThe Android TV Receiver SDK features native support for Ad Breaks and companion\nads within a given media stream.\n\nSee the [Web Receiver Ad Breaks Overview](/cast/docs/web_receiver/ad_breaks)\nfor more information on how Ad Breaks work.\n\nHandling load with ad breaks\n----------------------------\n\nOn your Android TV app, the breaks are included in\n[`MediaLoadRequestData`](/android/reference/com/google/android/gms/cast/MediaLoadRequestData).\nLoad requests can be processed normally and the\n[`AdBreakClipInfo`](/android/reference/com/google/android/gms/cast/AdBreakClipInfo)s\nand\n[`AdBreakInfo`](/android/reference/com/google/android/gms/cast/AdBreakInfo)s\ncan be retrieved from the\n[`MediaInfo`](/android/reference/com/google/android/gms/cast/MediaInfo):\nKotlin \n\n```kotlin\nclass MyMediaLoadCommandCallback : MediaLoadCommandCallback() {\n override fun onLoad( senderId: String?, loadRequestData: MediaLoadRequestData\n ): Task {\n return Tasks.call {\n // Resolve the entity into your data structure and load media.\n val mediaInfo = loadRequestData.mediaInfo\n ...\n myPrepareAdBreaks(mediaInfo.adBreakClips, mediaInfo.adBreaks)\n // Update media metadata and state (this clears all previous status\n // overrides).\n castReceiverContext.getMediaStatusModifier()\n .setDataFromLoad(mediaInfo) // Ad breaks are set on the modifier.\n castReceiverContext.getMediaManager().broadcastMediaStatus()\n // Return the resolved MediaLoadRequestData to indicate load success.\n return loadRequestData\n }\n }\n}\n```\nJava \n\n```gdscript\npublic class MyMediaLoadCommandCallback extends MediaLoadCommandCallback {\n @Override\n public Task onLoad(String senderId, MediaLoadRequestData loadRequestData) {\n return Tasks.call(() -\u003e {\n // Resolve the entity into your data structure and load media.\n MediaInfo mediaInfo = loadRequestData.getMediaInfo();\n ...\n myPrepareAdBreaks(mediaInfo.getAdBreakClips(), mediaInfo.getAdBreaks());\n // Update media metadata and state (this clears all previous status\n // overrides).\n castReceiverContext.getMediaStatusModifier()\n .setDataFromLoad(mediaInfo); // Ad breaks are set on the modifier.\n castReceiverContext.getMediaManager().broadcastMediaStatus();\n // Return the resolved MediaLoadRequestData to indicate load success.\n return loadRequestData;\n });\n }\n}\n```\n\nUpdating ad breaks\n------------------\n\nWhen ads start playing, update the\n[`AdBreakStatus`](/android/reference/com/google/android/gms/cast/AdBreakStatus)\non the\n[`MediaStatusModifier`](/android/reference/com/google/android/gms/cast/tv/media/MediaStatusModifier)\nto broadcast that your app has started playing ads:\nKotlin \n\n```kotlin\nval breakStatus = AdBreakStatus.Builder()\n .setBreakId(\"b1\")\n .setBreakClipId(\"bc1\")\n .setCurrentBreakClipTimeInMs(breakClipProgress)\n .setCurrentBreakTimeInMs(breakProgress)\n .setWhenSkippableInMs(5000) // Set this field so that the ad break clip is skippable\n .build()\n\ncastReceiverContext.getMediaStatusModifier()\n .setAdBreakStatus(breakStatus)\n```\nJava \n\n```java\nAdBreakStatus breakStatus =\n new AdBreakStatus.Builder()\n .setBreakId(\"b1\")\n .setBreakClipId(\"bc1\")\n .setCurrentBreakClipTimeInMs(breakClipProgress)\n .setCurrentBreakTimeInMs(breakProgress)\n .setWhenSkippableInMs(5000) // Set this field so that the ad break clip is skippable\n .build();\n\ncastReceiverContext.getMediaStatusModifier()\n .setAdBreakStatus(breakStatus);\n```\n\nYou can also dynamically modify the ad breaks after an item is loaded:\nKotlin \n\n```kotlin\nvar breakClip1: AdBreakClipInfo = ...\nvar breakClip2: AdBreakClipInfo = ...\nvar breakClip3: AdBreakClipInfo = ...\n\nvar break1: AdBreakInfo = ...\nvar break2: AdBreakInfo = ...\n\nmediaManager.getMediaStatusModifier().getMediaInfoModifier()\n .setAdBreakClips({breakClip1, breakClip2, breakClip3})\n .setAdBreaks({break1, break2})\n```\nJava \n\n```text\nAdBreakClipInfo breakClip1 = ...\nAdBreakClipInfo breakClip2 = ...\nAdBreakClipInfo breakClip3 = ...\n\nAdBreakInfo break1 = ...\nAdBreakInfo break2 = ...\n\nmediaManager.getMediaStatusModifier().getMediaInfoModifier()\n .setAdBreakClips({breakClip1, breakClip2, breakClip3})\n .setAdBreaks({break1, break2});\n```\n\nEnable and handle ad skipping\n-----------------------------\n\nWhen an ad break is playing, the senders will show a button to skip the current\nad break clip if it is skippable. To enable the ability for a user to skip an ad\nbreak clip, use the\n[`MediaStatusModifier`](/android/reference/com/google/android/gms/cast/tv/media/MediaStatusModifier)\nto add the\n[`COMMAND_SKIP_AD`](/android/reference/com/google/android/gms/cast/MediaStatus#public-static-final-long-command_skip_ad)\nmedia command:\nKotlin \n\n```kotlin\nmMediaManager.getMediaStatusModifier().setMediaCommandSupported(MediaStatus.COMMAND_SKIP_AD, true)\n```\nJava \n\n```scdoc\nmMediaManager.getMediaStatusModifier().setMediaCommandSupported(MediaStatus.COMMAND_SKIP_AD, true);\n```\n\nTo handle the `SKIP_AD` command, implement the\n[`onSkipAd`](/android/reference/com/google/android/gms/cast/tv/media/MediaCommandCallback#onSkipAd(java.lang.String,%20com.google.android.gms.cast.RequestData))\ncallback in your\n[`MediaCommandCallback`](/android/reference/com/google/android/gms/cast/tv/media/MediaCommandCallback)s:\nKotlin \n\n```kotlin\nclass MyMediaCommandCallback : MediaCommandCallback() {\n override fun onSkipAd(requestData: RequestData?): Task\u003cVoid?\u003e {\n // Skip your ad\n ...\n return Tasks.forResult\u003cAny?\u003e(null)\n }\n}\n\nval mediaManager = CastReceiverContext.getInstance().mediaManager\nmediaManager.setMediaCommandCallback(MyMediaCommandCallback())\n```\nJava \n\n```gdscript\npublic class MyMediaCommandCallback extends MediaCommandCallback {\n @Override\n public Task onSkipAd(RequestData requestData) {\n // Skip your ad\n ...\n return Tasks.forResult(null);\n }\n}\n\nMediaManager mediaManager =\n CastReceiverContext.getInstance().getMediaManager();\nmediaManager.setMediaCommandCallback(new MyMediaCommandCallback());\n```\n\nClient-side stitching\n---------------------\n\n[Client-side stitching](/cast/docs/caf_receiver/ad_breaks#client-side_ad_stitching)\nis where ads are not embedded in the stream. For Cast Connect, in addition to\nupdating the\n[`AdBreakStatus`](/android/reference/com/google/android/gms/cast/AdBreakStatus)\non the\n[`MediaStatusModifier`](/android/reference/com/google/android/gms/cast/tv/media/MediaStatusModifier)\nyou mustset the playback speed to 0 on the\n[`PlaybackStateCompat`](https://developer.android.com/reference/kotlin/android/support/v4/media/session/PlaybackStateCompat.Builder)\nso the senders know to freeze the content timeline progress.\nKotlin \n\n```kotlin\n// Playback speed should be 0 if content is not playing.\nif (adIsPlaying) {\n playbackSpeed = 0.0f\n}\nval stateBuilder = PlaybackStateCompat.Builder()\n .setActions(AVAILABLE_MEDIA_ACTIONS)\nstateBuilder.setState(playbackStateCompat, position, playbackSpeed)\nmediaSession.setPlaybackState(stateBuilder.build())\n```\nJava \n\n```scilab\n// Playback speed should be 0 if content is not playing.\nif (adIsPlaying) {\n playbackSpeed = 0.0f;\n}\nPlaybackStateCompat.Builder stateBuilder = new PlaybackStateCompat.Builder()\n .setActions(AVAILABLE_MEDIA_ACTIONS);\nstateBuilder.setState(playbackStateCompat, position, playbackSpeed);\nmediaSession.setPlaybackState(stateBuilder.build());\n```\n\nOnce the ad finishes, you should resume the previous playback speed.\n\nServer-side stitching\n---------------------\n\nFor\n[server-side stitching](/cast/docs/caf_receiver/ad_breaks#server-side_ad_stitching),\nthe ads are embedded so the server is expected to provide a single stream that\ncontains both the content and ads. In this case, playback can continue\nprogressing normally as the timeline contains the duration of the ad in addition\nto the content."]]