广告插播时间点
Android 发送方 SDK 支持在给定媒体串流中投放广告插播和随播广告。
如需详细了解广告插播的运作方式,请参阅网站接收器广告插播概览。
虽然您可以在发送器和接收器上指定广告插播时间点,但建议您在 Web 接收器和 Android TV 接收器上指定广告插播时间点,以便在各个平台上保持一致的行为。
在 Android 上,使用 AdBreakClipInfo
和 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)
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);
添加自定义操作
发件人应用可以扩展 MediaIntentReceiver
以处理自定义操作或替换其行为。如果您实现了自己的 MediaIntentReceiver
,则需要将其添加到清单中,并在 CastMediaOptions
中设置其名称。此示例提供了自定义操作,用于替换切换远程媒体播放、按媒体按钮和其他类型的操作。
// In AndroidManifest.xml
<receiver android:name="com.example.MyMediaIntentReceiver" />
// 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) { } }
// 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) { } }
添加自定义渠道
为了让发送方应用与接收方应用通信,您的应用需要创建一个自定义渠道。发送方可以使用自定义通道向接收方发送字符串消息。每个自定义渠道都由唯一的命名空间定义,并且必须以前缀 urn:x-cast:
开头,例如 urn:x-cast:com.example.custom
。您可以有多个自定义渠道,每个渠道都有一个唯一的命名空间。接收器应用还可以使用同一命名空间发送和接收消息。
自定义渠道是使用 Cast.MessageReceivedCallback
接口实现的:
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") } }
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); } }
将发送方应用连接到接收方应用后,即可使用 setMessageReceivedCallbacks
方法创建自定义渠道:
try { mCastSession.setMessageReceivedCallbacks( mHelloWorldChannel.namespace, mHelloWorldChannel) } catch (e: IOException) { Log.e(TAG, "Exception while creating channel", e) }
try { mCastSession.setMessageReceivedCallbacks( mHelloWorldChannel.getNamespace(), mHelloWorldChannel); } catch (IOException e) { Log.e(TAG, "Exception while creating channel", e); }
创建自定义通道后,发送方可以使用 sendMessage
方法通过该通道向接收方发送字符串消息:
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) } } }
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); } } }
支持自动播放
请参阅自动播放和队列 API 部分。
替换用户体验微件的图片选择
框架的各种组件(即 Cast 对话框、迷你控制器和 UIMediaController,如果已配置)将显示当前投放的媒体的海报图片。图片海报图片的网址通常包含在媒体的 MediaMetadata
中,但发件人应用可能有其他网址来源。
ImagePicker
类定义了一种方法,用于根据图片的用途(例如通知缩略图或全屏背景)从 MediaMetadata
中的图片列表中选择合适的图片。默认的 ImagePicker
实现始终选择第一张图片,如果 MediaMetadata
中没有可用图片,则返回 null。您的应用可以对 ImagePicker
进行子类化并替换 onPickImage(MediaMetadata, ImageHints)
方法以提供备选实现,然后使用 CastMediaOptions.Builder
的 setImagePicker
方法选择该子类。ImageHints
会向 ImagePicker
提供有关要选择在界面中显示的图片的类型和大小的提示。
自定义 Cast 对话框
管理会话生命周期
SessionManager
是管理会话生命周期的中心位置。SessionManager
会监听 Android MediaRouter
路线选择状态变化,以启动、恢复和结束会话。选择路线后,SessionManager
会创建 Session
对象,并尝试启动或恢复该对象。取消选择路线后,SessionManager
会结束当前会话。
因此,为确保 SessionManager
能够正确管理会话生命周期,您必须确保:
- 在路由选择器对话框中,当用户选择设备时调用
MediaRouter.selectRoute(MediaRouter.RouteInfo)
。 - 在路线控制器对话框(连接状态或投放状态)中,在用户停止投放时调用
MediaRouter.unselect(int)
。
根据您创建投放对话框的方式,您可能需要执行其他操作:
- 如果您使用
MediaRouteChooserDialog
和MediaRouteControllerDialog
创建 Cast 对话框,则这些对话框会自动更新MediaRouter
中的路线选择,因此您无需执行任何操作。 - 如果您使用
CastButtonFactory.setUpMediaRouteButton(Context, Menu, int)
或CastButtonFactory.setUpMediaRouteButton(Context, MediaRouteButton)
设置了投放按钮,则系统实际上会使用MediaRouteChooserDialog
和MediaRouteControllerDialog
创建对话框,因此您无需执行任何操作。 - 对于其他情况,您将创建自定义 Cast 对话框,因此需要按照上述说明更新
MediaRouter
中的路线选择状态。
零设备状态
如果您创建自定义 Cast 对话框,则自定义 MediaRouteChooserDialog
应正确处理找不到任何设备的情况。该对话框应包含指示器,以便用户清楚了解应用何时仍在尝试查找设备,以及何时不再尝试查找设备。
如果您使用的是默认的 MediaRouteChooserDialog
,则系统已处理零设备状态。
后续步骤
至此,您可以向 Android 发件人应用添加的功能已全部介绍完毕。现在,您可以为其他平台(iOS 或 Web)构建发件人应用,也可以构建Web 接收器应用。