为您的 Android 应用添加高级功能

广告插播时间点

Android 发送者 SDK 支持在给定媒体流内使用广告插播时间点和随播广告。

如需详细了解广告插播时间点的运作方式,请参阅网络接收器广告插播概览

虽然可以在发送端和接收端上同时指定广告插播时间点,但建议在网络接收器Android TV 接收器中指定中断,以确保各个平台的行为保持一致。

在 Android 上,使用 AdBreakClipInfoAdBreakInfo 在加载命令中指定广告插播时间点:

Kotlin
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);

添加自定义操作

发送方应用可以扩展 MediaIntentReceiver 来处理自定义操作或替换其行为。如果您已实现自己的 MediaIntentReceiver,则需要将其添加到清单中,并在 CastMediaOptions 中设置其名称。此示例提供了一些自定义操作,这些操作替换了切换远程媒体播放的开关、按媒体按钮以及其他类型的操作。

// 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) {
    }
}

添加自定义渠道

为了让发送方应用与接收方应用进行通信,您的应用需要创建一个自定义渠道。发送方可以使用自定义渠道向接收方发送字符串消息。每个自定义渠道都由唯一的命名空间定义,且必须以前缀 urn:x-cast: 开头,例如 urn:x-cast:com.example.custom。您可以拥有多个自定义渠道,每个自定义渠道都有一个唯一的命名空间。接收器应用还可以使用同一命名空间发送和接收消息

自定义渠道通过 Cast.MessageReceivedCallback 接口实现:

Kotlin
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);
    }
}

将发送者应用连接到接收者应用后,可以使用 setMessageReceivedCallbacks 方法创建自定义渠道:

Kotlin
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);
}

创建自定义渠道后,发送方可以使用 sendMessage 方法通过该渠道向接收方发送字符串消息:

Kotlin
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);
        }
    }
}

支持自动播放

请参阅自动播放和队列 API 部分。

替换用户体验 widget 的图片选择

框架的各个组件(即 Cast 对话框、迷你控制器和 UIMediaController,如果已配置)将显示当前投射媒体的海报图片。图片图片的网址通常包含在媒体的 MediaMetadata 中,但发送器应用可能有备用网址来源。

ImagePicker 类定义了一种方式,用于根据图片的用途(例如通知缩略图或全屏背景),从 MediaMetadata 的图片列表中选择适当的图片。默认的 ImagePicker 实现始终会选择第一张图片,如果 MediaMetadata 中没有可用的图片,则返回 null。您的应用可以创建 ImagePicker 的子类并替换 onPickImage(MediaMetadata, ImageHints) 方法以提供备用实现,然后使用 CastMediaOptions.BuildersetImagePicker 方法选择该子类。ImageHints 会向 ImagePicker 提供关于要在界面中选择显示的图片的类型和大小的提示。

自定义 Cast 对话框

管理会话生命周期

SessionManager 是管理会话生命周期的中心位置。SessionManager 会监听 Android MediaRouter 路由选择状态变化,以启动、恢复和结束会话。选择路线后,SessionManager 将创建一个 Session 对象,并尝试启动或恢复该路线。如果未选择路由,SessionManager 将结束当前会话。

因此,为确保 SessionManager 正确管理会话生命周期,您必须确保:

根据您创建 Cast 对话框的方式,您可能还需要执行其他操作:

零设备状态

如果您创建自定义 Cast 对话框,您的自定义 MediaRouteChooserDialog 应该会正确处理未找到设备的情况。当您的应用仍在尝试查找设备以及发现尝试不再处于活跃状态时,该对话框应该具有指示标志,以便向用户清楚说明。

如果您使用的是默认 MediaRouteChooserDialog,则零设备状态已经过处理。

后续步骤

以上就是您可以添加到 Android “发送器”应用的功能。 您现在可以针对其他平台(iOSWeb)构建发送器应用,或者构建网络接收器应用