Warning: The IMA SDK for Cast SDK v2 has been officially deprecated, as of November 17th, 2021. All existing users are urged to migrate to CAF native Ad Breaks.

Android 发送者应用

Stay organized with collections Save and categorize content based on your preferences.

本指南将通过一个示例应用,向您介绍如何创建支持 Cast 的 Android 发送者应用。

如需按照本指南操作,请下载我们的 AdvancedExample.zip,这是一款完整的 Android 应用,可通过 IMA Android SDK 为包含广告的视频实现投射功能。

前提条件

本指南以您对 Google Cast SDK 和 IMA SDK for Android 的现有知识为基础。用户应熟悉以下内容:

本指南将讨论高级示例中使用的与接收者通信的具体设置。未涵盖标准投放设置代码或常规 IMA 广告播放。如需详细了解如何实现接收器,请参阅我们的 HTML5 接收器指南

媒体播放器

在 CastApplication.java 中实现了将媒体加载到接收器的功能,以及大部分 Cast 专用代码。此尝试加载的媒体是内容网址,还会向接收者发送自定义消息,告知其使用同一广告代码请求广告,并跳转到发送者的当前时间戳。

CastApplication.java

private void createMediaPlayer() {
    Log.d(TAG, "making media player");
    // Create a Remote Media Player
    mRemoteMediaPlayer = new RemoteMediaPlayer();

    mRemoteMediaPlayer.setOnStatusUpdatedListener(
            new RemoteMediaPlayer.OnStatusUpdatedListener() {
                @Override
                public void onStatusUpdated() {
                    MediaStatus mediaStatus = mRemoteMediaPlayer.getMediaStatus();
                    Log.d(TAG, "Media status: " + mediaStatus);
                }
            });

    mRemoteMediaPlayer.setOnMetadataUpdatedListener(
            new RemoteMediaPlayer.OnMetadataUpdatedListener() {
                @Override
                public void onMetadataUpdated() {
                    MediaInfo mediaInfo = mRemoteMediaPlayer.getMediaInfo();
                    Log.d(TAG, "Media info: " + mediaInfo);
                }
            });

    try {
        Cast.CastApi.setMessageReceivedCallbacks(sApiClient,
                mRemoteMediaPlayer.getNamespace(), mRemoteMediaPlayer);
    } catch (IOException e) {
        Log.e(TAG, "Exception while creating media channel", e);
    }

    mRemoteMediaPlayer
            .requestStatus(sApiClient)
            .setResultCallback(
                    new ResultCallback<RemoteMediaPlayer.MediaChannelResult>() {
                        @Override
                        public void onResult(RemoteMediaPlayer.MediaChannelResult result) {
                            if (!result.getStatus().isSuccess()) {
                                Log.e(TAG, "Failed to request status.");
                            } else {
                                MediaMetadata mediaMetadata =
                                        new MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE);
                                mediaMetadata.putString(MediaMetadata.KEY_TITLE, "My video");
                                MediaInfo mediaInfo = new MediaInfo.Builder(mContentUrl)
                                        .setContentType("video/mp4")
                                        .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
                                        .setMetadata(mediaMetadata)
                                        .build();
                                loadMedia(mediaInfo, false);
                            }
                        }
                    });
}


private void loadMedia(MediaInfo mediaInfo, Boolean autoplay) {
    try {
        Log.d(TAG, "loading media");
        mRemoteMediaPlayer.load(sApiClient, mediaInfo, autoplay)
                .setResultCallback(new ResultCallback<RemoteMediaPlayer.MediaChannelResult>() {
                    @Override
                    public void onResult(RemoteMediaPlayer.MediaChannelResult result) {
                        if (result.getStatus().isSuccess()) {
                            boolean adStarted = mVideoPlayerController.hasAdStarted();
                            if (mVideoFragment.isVmap() || !adStarted) {
                                sendMessage("requestAd," + mAdTagUrl + ","
                                        + mVideoPlayerController.getCurrentContentTime());
                            } else {
                                sendMessage("seek,"
                                    + mVideoPlayerController.getCurrentContentTime());
                            }
                        } else {
                            Log.e(TAG, "Error loading Media : "
                                    + result.getStatus().getStatusCode());
                        }
                    }
                });
    } catch (Exception e) {
        Log.e(TAG, "Problem opening media during loading", e);
    }
}

private void sendMessage(String message) {
    if (sApiClient != null && incomingMsgHandler != null) {
        try {
            Log.d(TAG, "Sending message: " + message);
            Cast.CastApi.sendMessage(sApiClient, NAMESPACE, message)
                    .setResultCallback(new ResultCallback<Status>() {
                        @Override
                        public void onResult(Status result) {
                            if (!result.isSuccess()) {
                                Log.e(TAG, "Sending message failed");
                            }
                        }
                    });
        } catch (Exception e) {
            Log.e(TAG, "Exception while sending message", e);
        }
    }
}

onResult 回调中定义了两条消息:seek(用于在没有广告的情况下在接收设备上继续播放)和 requestAd(在接收器上重新请求相同的广告代码)。在该示例中,requestAd 消息用于广告播放列表和 VMAP,但您可以自行定义,以便在投放时执行的操作。这些消息以英文逗号分隔,其中第一个参数是消息名称,其余参数是广告代码和跳转时间。

MediaRouter 回调

MediaRouter.Callback 可处理开始和停止投射操作,并控制在 Android 和投射设备之间切换视频播放功能。这涉及到暂停和跳转视频。

CastApplication.java

private final MediaRouter.Callback mediaRouterCallback = new MediaRouter.Callback() {
    @Override
    public void onRouteSelected(MediaRouter router, MediaRouter.RouteInfo info) {
        // User starts casting. Pause video on device and set Google Cast device.
        mVideoPlayerController = mVideoFragment.getVideoPlayerController();
        mVideoPlayerController.pause();
        mAdTagUrl = mVideoPlayerController.getAdTagUrl();
        mContentUrl = mVideoPlayerController.getContentVideoUrl();
        CastDevice device = CastDevice.getFromBundle(info.getExtras());
        setSelectedDevice(device);
    }

    @Override
    public void onRouteUnselected(MediaRouter router, MediaRouter.RouteInfo info) {
        // User stops casting. Resume video on device and seek to current time of Google Cast.
        mVideoPlayerController.resume();
        if (mCastAdPlaying) {
            mVideoPlayerController.seek(mCastContentTime);
        } else {
            double videoPosition = mRemoteMediaPlayer.getApproximateStreamPosition();
            mVideoPlayerController.seek(videoPosition / 1000.0);
        }

        stopCastApplication();
        setSelectedDevice(null);
    }
};

信息

应用会定义 MessageReceivedCallback,以便接收端提供投射视频的时间更新通知以及投放的广告停止播放和内容开始播放的时间。这些消息由接收方发送,并由两条消息(onContentPauseRequestedonContentResumeRequested)处理。这些消息采用逗号分隔,格式类似于上述 seekrequestAd 消息。

CastApplication.java

public final Cast.MessageReceivedCallback incomingMsgHandler =
        new Cast.MessageReceivedCallback() {
            @Override
            public void onMessageReceived(CastDevice castDevice, String namespace,
                                          String message) {
                Log.d(TAG, "Receiving message: " + message);
                String[] splitMessage = message.split(",");
                String event = splitMessage[0];
                switch (event) {
                    case "onContentPauseRequested":
                        mCastAdPlaying = true;
                        mCastContentTime = Double.parseDouble(splitMessage[1]);
                        return;
                    case "onContentResumeRequested":
                        mCastAdPlaying = false;
                        return;
                }
            }
        };

后续步骤

现在,您已经非常了解 Android 发送器应用如何与接收端进行交互。如需试用,请创建接收器应用并注册接收器,以接收来自发送者应用的 API 调用的应用 ID。