The Android Sender SDK provides 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.
While breaks can be specified on both the sender and receiver, it is recommended for them to be specified on the Web Receiver and Android TV Receiver to maintain consistent behavior across platforms.
On Android, specify ad breaks in a load command using AdBreakClipInfo
and AdBreakInfo
:
val breakClip1: AdBreakClipInfo = AdBreakClipInfo.Builder("bc0") .setTitle("Clip title") .setPosterUrl("https://www.some.url") .setDuration(60000) .setWhenSkippableInMs(5000) // Set this field so that the ad is skippable .build() val breakClip2: AdBreakClipInfo = … val breakClip3: AdBreakClipInfo = … val break1: AdBreakClipInfo = AdBreakInfo.Builder(/* playbackPositionInMs= */ 10000) .setId("b0") .setBreakClipIds({"bc0","bc1","bc2"}) … .build() val mediaInfo: MediaInfo = MediaInfo.Builder() … .setAdBreaks({break1}) .setAdBreakClips({breakClip1, breakClip2, breakClip3}) .build() val mediaLoadRequestData: MediaLoadRequestData = MediaInfo.Builder() … .setMediaInfo(mediaInfo) .build() remoteMediaClient.load(mediaLoadRequestData)Java
AdBreakClipInfo breakClip1 = new AdBreakClipInfo.Builder("bc0") .setTitle("Clip title") .setPosterUrl("https://www.some.url") .setDuration(60000) .setWhenSkippableInMs(5000) // Set this field so that the ad is skippable .build(); AdBreakClipInfo breakClip2 = … AdBreakClipInfo breakClip3 = … AdBreakInfo break1 = new AdBreakInfo.Builder(/* playbackPositionInMs= */ 10000) .setId("b0") .setBreakClipIds({"bc0","bc1","bc2"}) … .build(); MediaInfo mediaInfo = new MediaInfo.Builder() … .setAdBreaks({break1}) .setAdBreakClips({breakClip1, breakClip2, breakClip3}) .build(); MediaLoadRequestData mediaLoadRequestData = new MediaInfo.Builder() … .setMediaInfo(mediaInfo) .build(); remoteMediaClient.load(mediaLoadRequestData);Add custom actions
A sender app can extend MediaIntentReceiver
to handle custom actions or override its behavior. If you have implemented your own MediaIntentReceiver
, you need to add it to the manifest, and also set its name in the CastMediaOptions
. This example provides custom actions that override toggle remote media playback, pressing the media button and other types of actions.
// In AndroidManifest.xml
<receiver android:name="com.example.MyMediaIntentReceiver" />
Kotlin
// In your OptionsProvider var mediaOptions = CastMediaOptions.Builder() .setMediaIntentReceiverClassName(MyMediaIntentReceiver::class.java.name) .build() // Implementation of MyMediaIntentReceiver internal class MyMediaIntentReceiver : MediaIntentReceiver() { override fun onReceiveActionTogglePlayback(currentSession: Session) { } override fun onReceiveActionMediaButton(currentSession: Session, intent: Intent) { } override fun onReceiveOtherAction(context: Context?, action: String, intent: Intent) { } }Java
// In your OptionsProvider CastMediaOptions mediaOptions = new CastMediaOptions.Builder() .setMediaIntentReceiverClassName(MyMediaIntentReceiver.class.getName()) .build(); // Implementation of MyMediaIntentReceiver class MyMediaIntentReceiver extends MediaIntentReceiver { @Override protected void onReceiveActionTogglePlayback(Session currentSession) { } @Override protected void onReceiveActionMediaButton(Session currentSession, Intent intent) { } @Override protected void onReceiveOtherAction(Context context, String action, Intent intent) { } }Add a custom channel
For the sender app to communicate with the receiver app, your app needs to create a custom channel. The sender can use the custom channel to send string messages to the receiver. Each custom channel is defined by a unique namespace and must start with the prefix urn:x-cast:
, for example, urn:x-cast:com.example.custom
. It is possible to have multiple custom channels, each with a unique namespace. The receiver app can also send and receive messages using the same namespace.
The custom channel is implemented with the Cast.MessageReceivedCallback
interface:
class HelloWorldChannel : MessageReceivedCallback { val namespace: String get() = "urn:x-cast:com.example.custom" override fun onMessageReceived(castDevice: CastDevice, namespace: String, message: String) { Log.d(TAG, "onMessageReceived: $message") } }Java
class HelloWorldChannel implements Cast.MessageReceivedCallback { public String getNamespace() { return "urn:x-cast:com.example.custom"; } @Override public void onMessageReceived(CastDevice castDevice, String namespace, String message) { Log.d(TAG, "onMessageReceived: " + message); } }Note: This procedure does not use the
setSupportedNamespaces()
method.
Once the sender app is connected to the receiver app, the custom channel can be created using the setMessageReceivedCallbacks
method:
try { mCastSession.setMessageReceivedCallbacks( mHelloWorldChannel.namespace, mHelloWorldChannel) } catch (e: IOException) { Log.e(TAG, "Exception while creating channel", e) }Java
try { mCastSession.setMessageReceivedCallbacks( mHelloWorldChannel.getNamespace(), mHelloWorldChannel); } catch (IOException e) { Log.e(TAG, "Exception while creating channel", e); }
Once the custom channel is created, the sender can use the sendMessage
method to send string messages to the receiver over that channel:
private fun sendMessage(message: String) { if (mHelloWorldChannel != null) { try { mCastSession.sendMessage(mHelloWorldChannel.namespace, message) .setResultCallback { status -> if (!status.isSuccess) { Log.e(TAG, "Sending message failed") } } } catch (e: Exception) { Log.e(TAG, "Exception while sending message", e) } } }Java
private void sendMessage(String message) { if (mHelloWorldChannel != null) { try { mCastSession.sendMessage(mHelloWorldChannel.getNamespace(), message) .setResultCallback( status -> { if (!status.isSuccess()) { Log.e(TAG, "Sending message failed"); } }); } catch (Exception e) { Log.e(TAG, "Exception while sending message", e); } } }Supporting autoplay
See the section Autoplay & Queueing APIs.
Various components of the framework (namely the Cast dialog, the mini controller, and the UIMediaController, if so configured) will display artwork for the currently casting media. The URLs to the image artwork are typically included in the MediaMetadata
for the media, but the sender app may have an alternate source for the URLs.
The ImagePicker
class defines a means for selecting an appropriate image from the list of images in a MediaMetadata
, based on the use of the image, for example, notification thumbnail or full screen background. The default ImagePicker
implementation always chooses the first image, or returns null if no image is available in the MediaMetadata
. Your app can subclass ImagePicker
and override the onPickImage(MediaMetadata, ImageHints)
method to provide an alternate implementation, and then select that subclass with the setImagePicker
method of CastMediaOptions.Builder
. ImageHints
provides hints to an ImagePicker
about the type and size of an image to be selected for display in the UI.
SessionManager
is the central place for managing session lifecycle. SessionManager
listens to Android MediaRouter
route selection state changes to start, resume and end sessions. When a route is selected, SessionManager
will create a Session
object and tries to start or resume it. When a route is unselected, SessionManager
will end the current session.
Therefore, to ensure SessionManager
manages session lifecycles properly, you must make sure that:
MediaRouter.selectRoute(MediaRouter.RouteInfo)
when a user selects a device.MediaRouter.unselect(int)
when the user stops casting.Depending on how you create the Cast dialogs, additional actions may need to be done:
MediaRouteChooserDialog
and MediaRouteControllerDialog
, then these dialogs will update route selection in MediaRouter
automatically, so nothing needs to be done.CastButtonFactory.setUpMediaRouteButton(Context, Menu, int)
or CastButtonFactory.setUpMediaRouteButton(Context, MediaRouteButton)
, then the dialogs are actually created using MediaRouteChooserDialog
and MediaRouteControllerDialog
, so nothing needs to be done, too.MediaRouter
.If you create custom Cast dialogs, your custom MediaRouteChooserDialog
should properly handle the case of zero devices being found. The dialog should have indicators making it clear to your users when your app is still attempting to find devices and when the discovery attempt is no longer active.
If you are using the default MediaRouteChooserDialog
, the zero devices state is already handled.
This concludes the features that you can add to your Android Sender app. You can now build a sender app for another platform (iOS or Web), or build a Web Receiver app.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.3